在Python中,如何在重新加载后更改实例化对象?

发布于 2024-07-25 22:30:22 字数 648 浏览 3 评论 0原文

假设您有一个从模块内的类实例化的对象。 现在,您重新加载该模块。 您要做的下一件事是使重新加载影响该类。

mymodule.py
---
class ClassChange():
    def run(self):
        print 'one'

myexperiment.py
---
import mymodule
from mymodule import ClassChange  # why is this necessary?
myObject = ClassChange()
myObject.run()
>>> one
### later, i changed this file, so that it says print 'two'

reload(mymodule)
# trick to change myObject needed here
myObject.run()
>>> two

您是否必须创建一个新的 ClassChange 对象,将 myObject 复制到其中,然后删除旧的 myObject? 或者有更简单的方法吗?

编辑: run() 方法似乎是一个静态类样式方法,但这只是为了简洁起见。 我希望 run() 方法对对象内部的数据进行操作,因此静态模块函数不会这样做......

Let's say you have an object that was instantiated from a class inside a module.
Now, you reload that module.
The next thing you'd like to do is make that reload affect that class.

mymodule.py
---
class ClassChange():
    def run(self):
        print 'one'

myexperiment.py
---
import mymodule
from mymodule import ClassChange  # why is this necessary?
myObject = ClassChange()
myObject.run()
>>> one
### later, i changed this file, so that it says print 'two'

reload(mymodule)
# trick to change myObject needed here
myObject.run()
>>> two

Do you have to make a new ClassChange object, copy myObject into that, and delete the old myObject? Or is there a simpler way?

Edit: The run() method seems like a static class style method but that was only for the sake of brevity. I'd like the run() method to operate on data inside the object, so a static module function wouldn't do...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

小镇女孩 2024-08-01 22:30:22

要更新类的所有实例,有必要在某个地方跟踪这些实例——通常通过弱引用(弱值字典是最方便和通用的),因此“跟踪”功能不会阻止不需要的实例消失,当然!

您通常希望在类对象中保留这样的容器,但是,在这种情况下,由于您将重新加载模块,因此获取旧的类对象并不简单; 在模块级别工作更简单。

因此,假设一个“可升级模块”需要在开始时定义一个弱值字典(以及一个辅助的“下一个要使用的键”int),并带有常规名称:

import weakref
class _List(list): pass   # a weakly-referenceable sequence
_objs = weakref.WeakValueDictionary()
_nextkey = 0
def _register(obj):
  _objs[_nextkey] = List((obj, type(obj).__name__))
  _nextkey += 1

模块中的每个类通常必须具有在__init__中,调用_register(self)来注册新实例。

现在,“重新加载函数”可以在重新加载模块之前通过获取 _objs 的副本来获取该模块中所有类的所有实例的名册。

如果所需要的只是更改代码,那么生活相当容易:

def reload_all(amodule):
    objs = getattr(amodule, '_objs', None)
    reload(amodule)
    if not objs: return  # not an upgraable-module, or no objects
    newobjs = getattr(amodule, '_objs', None)
    for obj, classname in objs.values():
        newclass = getattr(amodule, classname)
        obj.__class__ = newclass
        if newobjs: newobjs._register(obj)

唉,人们通常确实希望给新类一个机会将旧类的对象更精细地升级为自身,例如通过合适的类方法。 这也不是太难:

def reload_all(amodule):
    objs = getattr(amodule, '_objs', None)
    reload(amodule)
    if not objs: return  # not an upgraable-module, or no objects
    newobjs = getattr(amodule, '_objs', None)
    for obj, classname in objs:
        newclass = getattr(amodule, classname)
        upgrade = getattr(newclass, '_upgrade', None)
        if upgrade:
            upgrade(obj)
        else:
            obj.__class__ = newclass
        if newobjs: newobjs._register(obj)

例如,假设类 Zap 的新版本已将属性从 foo 重命名为 bar。 这可能是新 Zap 的代码:

class Zap(object):
    def __init__(self):
        _register(self)
        self.bar = 23

    @classmethod
    def _upgrade(cls, obj):
        obj.bar = obj.foo
        del obj.foo
        obj.__class__ = cls

这还不是全部——关于这个主题还有很多话要说——但是,这是要点,答案已经足够长了(而且我已经筋疲力尽了;- )。

To update all instances of a class, it is necessary to keep track somewhere about those instances -- typically via weak references (weak value dict is handiest and general) so the "keeping track" functionality won't stop unneeded instances from going away, of course!

You'd normally want to keep such a container in the class object, but, in this case, since you'll be reloading the module, getting the old class object is not trivial; it's simpler to work at module level.

So, let's say that an "upgradable module" needs to define, at its start, a weak value dict (and an auxiliary "next key to use" int) with, say, conventional names:

import weakref
class _List(list): pass   # a weakly-referenceable sequence
_objs = weakref.WeakValueDictionary()
_nextkey = 0
def _register(obj):
  _objs[_nextkey] = List((obj, type(obj).__name__))
  _nextkey += 1

Each class in the module must have, typically in __init__, a call _register(self) to register new instances.

Now the "reload function" can get the roster of all instances of all classes in this module by getting a copy of _objs before it reloads the module.

If all that's needed is to change the code, then life is reasonably easy:

def reload_all(amodule):
    objs = getattr(amodule, '_objs', None)
    reload(amodule)
    if not objs: return  # not an upgraable-module, or no objects
    newobjs = getattr(amodule, '_objs', None)
    for obj, classname in objs.values():
        newclass = getattr(amodule, classname)
        obj.__class__ = newclass
        if newobjs: newobjs._register(obj)

Alas, one typically does want to give the new class a chance to upgrade an object of the old class to itself more finely, e.g. by a suitable class method. That's not too hard either:

def reload_all(amodule):
    objs = getattr(amodule, '_objs', None)
    reload(amodule)
    if not objs: return  # not an upgraable-module, or no objects
    newobjs = getattr(amodule, '_objs', None)
    for obj, classname in objs:
        newclass = getattr(amodule, classname)
        upgrade = getattr(newclass, '_upgrade', None)
        if upgrade:
            upgrade(obj)
        else:
            obj.__class__ = newclass
        if newobjs: newobjs._register(obj)

For example, say the new version of class Zap has renamed an attribute from foo to bar. This could be the code of the new Zap:

class Zap(object):
    def __init__(self):
        _register(self)
        self.bar = 23

    @classmethod
    def _upgrade(cls, obj):
        obj.bar = obj.foo
        del obj.foo
        obj.__class__ = cls

This is NOT all -- there's a LOT more to say on the subject -- but, it IS the gist, and the answer is WAY long enough already (and I, exhausted enough;-).

浅黛梨妆こ 2024-08-01 22:30:22

你必须创建一个新对象。 没有办法神奇地更新现有对象。

阅读 reload 内置文档 - 它非常清楚。 这是最后一段:

如果模块实例化类的实例,则重新加载定义该类的模块不会影响实例的方法定义 - 它们将继续使用旧的类定义。 派生类也是如此。

文档中还有其他警告,因此您确实应该阅读它,并考虑替代方案。 也许您想开始一个新问题,询问为什么要使用reload,并询问实现相同目标的其他方法。

You have to make a new object. There's no way to magically update the existing objects.

Read the reload builtin documentation - it is very clear. Here's the last paragraph:

If a module instantiates instances of a class, reloading the module that defines the class does not affect the method definitions of the instances — they continue to use the old class definition. The same is true for derived classes.

There are other caveats in the documentation, so you really should read it, and consider alternatives. Maybe you want to start a new question with why you want to use reload and ask for other ways of achieving the same thing.

小帐篷 2024-08-01 22:30:22

我的方法如下:

  1. 查看所有导入的模块,并仅重新加载具有新 .py 文件的模块(与现有 .pyc 文件相比)
  2. 对于重新加载的每个函数和类方法,设置 old_function.__code__ = new_function。 __代码__。
  3. 对于每个重新加载的类,使用 gc.get_referrers 列出该类的实例并将其 __class__ 属性设置为新版本。

这种方法的优点是:

  • 通常不需要以任何特定顺序重新加载模块
  • 通常只需要重新加载代码已更改的模块,无需
  • 修改类来跟踪其实例

您可以阅读有关该技术的信息(以及它的局限性)在这里:
http://luke-campagnola.blogspot.com /2010/12/easy-automated-reloading-in-python.html

您可以在这里下载代码:
http://luke.campagnola.me/code/downloads/reload.py

My approach to this is the following:

  1. Look through all imported modules and reload only those with a new .py file (as compared to the existing .pyc file)
  2. For every function and class method that is reloaded, set old_function.__code__ = new_function.__code__.
  3. For every reloaded class, use gc.get_referrers to list instances of the class and set their __class__ attribute to the new version.

Advantages to this approach are:

  • Usually no need to reload modules in any particular order
  • Usually only need to reload the modules with changed code and no more
  • Don't need to modify classes to keep track of their instances

You can read about the technique (and its limitations) here:
http://luke-campagnola.blogspot.com/2010/12/easy-automated-reloading-in-python.html

And you can download the code here:
http://luke.campagnola.me/code/downloads/reload.py

胡渣熟男 2024-08-01 22:30:22

您必须从新模块中获取新类并将其分配回实例。

如果您可以在使用带有此 mixin 的实例时随时触发此操作:

import sys

class ObjDebug(object):
  def __getattribute__(self,k):
    ga=object.__getattribute__
    sa=object.__setattr__
    cls=ga(self,'__class__')
    modname=cls.__module__ 
    mod=__import__(modname)
    del sys.modules[modname]
    reload(mod)
    sa(self,'__class__',getattr(mod,cls.__name__))
    return ga(self,k)

You have to get the new class from the fresh module and assign it back to the instance.

If you could trigger this operation anytime you use an instance with this mixin:

import sys

class ObjDebug(object):
  def __getattribute__(self,k):
    ga=object.__getattribute__
    sa=object.__setattr__
    cls=ga(self,'__class__')
    modname=cls.__module__ 
    mod=__import__(modname)
    del sys.modules[modname]
    reload(mod)
    sa(self,'__class__',getattr(mod,cls.__name__))
    return ga(self,k)
七婞 2024-08-01 22:30:22

以下代码可以实现您想要的功能,但是请不要使用它(至少在您非常确定自己做的是正确的事情之前不要使用它),我将其发布用于仅用于解释目的

mymodule.py:myexperiment.py

class ClassChange():
    @classmethod
    def run(cls,instance):
        print 'one',id(instance)

import mymodule

myObject = mymodule.ClassChange()
mymodule.ClassChange.run(myObject)

# change mymodule.py here

reload(mymodule)
mymodule.ClassChange.run(myObject)

在代码中实例化myObject时,您将获得ClassChange的实例。 该实例有一个名为 run实例方法。 即使在重新加载时,该对象也会保留此实例方法(原因由 nosklo 解释),因为重新加载只会重新加载 ClassChange

在上面的我的代码中,run是一个类方法。 类方法始终绑定到类(而不是实例)并对其进行操作(这就是为什么它们的第一个参数通常称为 cls,而不是 self)。 Wenn ClassChange 被重新加载,这个类方法也是如此。

您可以看到,我还将实例作为参数传递,以使用正确(相同)的 ClassChange 实例。 您可以看到这一点,因为在这两种情况下都打印了相同的对象 ID。

The following code does what you want, but please don't use it (at least not until you're very sure you're doing the right thing), I'm posting it for explanation purposes only.

mymodule.py:

class ClassChange():
    @classmethod
    def run(cls,instance):
        print 'one',id(instance)

myexperiment.py:

import mymodule

myObject = mymodule.ClassChange()
mymodule.ClassChange.run(myObject)

# change mymodule.py here

reload(mymodule)
mymodule.ClassChange.run(myObject)

When in your code you instanciate myObject, you get an instance of ClassChange. This instance has an instance method called run. The object keeps this instance method (for the reason explained by nosklo) even when reloading, because reloading only reloads the class ClassChange.

In my code above, run is a class method. Class methods are always bound to and operate on the class, not the instance (which is why their first argument is usually called cls, not self). Wenn ClassChange is reloaded, so is this class method.

You can see that I also pass the instance as an argument to work with the correct (same) instance of ClassChange. You can see that because the same object id is printed in both cases.

瞎闹 2024-08-01 22:30:22

我不确定这是否是最好的方法,或者是否符合您想要做的事情......但这可能对您有用。 如果您想更改某个方法的行为,对于某种类型的所有对象......只需使用函数变量即可。 例如:


def default_behavior(the_object):
  print "one"

def some_other_behavior(the_object):
  print "two"

class Foo(object):
  # Class variable: a function that has the behavior
  # (Takes an instance of a Foo as argument)
  behavior = default_behavior

  def __init__(self):
    print "Foo initialized"

  def method_that_changes_behavior(self):
    Foo.behavior(self)

if __name__ == "__main__":
  foo = Foo()
  foo.method_that_changes_behavior() # prints "one"
  Foo.behavior = some_other_behavior
  foo.method_that_changes_behavior() # prints "two"

  # OUTPUT
  # Foo initialized
  # one
  # two

您现在可以有一个类负责重新加载模块,并在重新加载后将 Foo.behavior 设置为新的内容。 我尝试了这段代码。 它工作正常:-)。

这对你有用吗?

I'm not sure if this is the best way to do it, or meshes with what you want to do... but this may work for you. If you want to change the behavior of a method, for all objects of a certain type... just use a function variable. For example:


def default_behavior(the_object):
  print "one"

def some_other_behavior(the_object):
  print "two"

class Foo(object):
  # Class variable: a function that has the behavior
  # (Takes an instance of a Foo as argument)
  behavior = default_behavior

  def __init__(self):
    print "Foo initialized"

  def method_that_changes_behavior(self):
    Foo.behavior(self)

if __name__ == "__main__":
  foo = Foo()
  foo.method_that_changes_behavior() # prints "one"
  Foo.behavior = some_other_behavior
  foo.method_that_changes_behavior() # prints "two"

  # OUTPUT
  # Foo initialized
  # one
  # two

You can now have a class that is responsible for reloading modules, and after reloading, setting Foo.behavior to something new. I tried out this code. It works fine :-).

Does this work for you?

狂之美人 2024-08-01 22:30:22

有一些技巧可以让你想要的事情成为可能。

有人已经提到,您可以拥有一个保留其实例列表的类,然后在重新加载时将每个实例的类更改为新实例。

然而,这效率不高。 更好的方法是更改​​旧类,使其与新类相同。

There are tricks to make what you want possible.

Someone already mentioned that you can have a class that keeps a list of its instances, and then changing the class of each instance to the new one upon reload.

However, that is not efficient. A better method is to change the old class so that it is the same as the new class.

只等公子 2024-08-01 22:30:22

这是 Python 3 的 @riccardo 答案的一个版本,并附有示例。

请注意,我们不需要 del sys.modules[modname],这实际上会导致错误。

ObjDebug.py

import sys
import importlib

class ObjDebug(object):
  def __getattribute__(self,k):
    ga=object.__getattribute__
    sa=object.__setattr__
    cls=ga(self,'__class__')
    modname=cls.__module__ 
    mod=__import__(modname)
    importlib.reload(mod)
    sa(self,'__class__',getattr(mod,cls.__name__))
    return ga(self,k)

示例:

MyClass.py

from ObjDebug import ObjDebug

class MyClass(ObjDebug):
    def __init__(self):
        self.a = 1
    def myget(self):
        c = 3
        return self.a*c

Python 控制台

➜ python3
Python 3.11.5 (main, Sep  7 2023, 08:22:18) [GCC 13.2.1 20230801] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from MyClass import MyClass
>>> myobj = MyClass()
>>> myobj.myget()
3
>>> # change `c = 3` to `c = 4`
>>> myobj.myget()
4

Here is a version of @riccardo answer for Python 3, with an example.

Notice we don't need the del sys.modules[modname], which in fact will result in an error.

ObjDebug.py

import sys
import importlib

class ObjDebug(object):
  def __getattribute__(self,k):
    ga=object.__getattribute__
    sa=object.__setattr__
    cls=ga(self,'__class__')
    modname=cls.__module__ 
    mod=__import__(modname)
    importlib.reload(mod)
    sa(self,'__class__',getattr(mod,cls.__name__))
    return ga(self,k)

Example:

MyClass.py

from ObjDebug import ObjDebug

class MyClass(ObjDebug):
    def __init__(self):
        self.a = 1
    def myget(self):
        c = 3
        return self.a*c

Python console

➜ python3
Python 3.11.5 (main, Sep  7 2023, 08:22:18) [GCC 13.2.1 20230801] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from MyClass import MyClass
>>> myobj = MyClass()
>>> myobj.myget()
3
>>> # change `c = 3` to `c = 4`
>>> myobj.myget()
4

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文