在Python,你怎么重载后更改实例化对象?(In Python, how do you chang

2019-07-29 14:58发布

比方说,你有一个从一个模块内的类实例化一个对象。 现在,你重新加载模块。 你想这样做的下一件事是做出重装影响类。

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()方法来对物体内部的数据进行操作,所以静态模块功能不会做......

Answer 1:

要更新类的所有实例,有必要保持对那些情况下轨道的地方 - 通常是通过弱引用(弱值字典是最方便和一般),因此“跟踪”功能将不消失停止不需要的情况下,当然!

通常你会想保持这样的容器类对象,但是,在这种情况下,你会被重新加载模块,让旧的类对象是不平凡的; 这是简单的模块级工作。

所以,让我们说这是一个“升级模块”需要定义,在其开始,弱值字典(和辅助“下一个键使用” 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)

例如,假设类扎普的新版本已经改名,从foo到酒吧的属性。 这可能是新的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

这还不是全部 - 还有很多更多的话要说 - 但是,它的要点,并回答是足够WAY已久的(和我,用尽足够;-)。



Answer 2:

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

阅读reload内置文档 -这是很清楚的。 这里的最后一段:

如果一个模块实例化一个类的实例,重装定义类不会影响实例的方法定义的模块-他们继续使用旧的类定义。 这同样适用于派生类如此。

有文档中的其他注意事项,让您真正应该看它,并考虑替代方案。 也许你要开始与你为什么要使用一个新的问题, reload ,并要求获得同样的事情的其他方式。



Answer 3:

我对这种做法如下:

  1. 翻阅所有导入模块,只有重装那些具有新的.py文件(相对于现有的.pyc文件的文件)
  2. 对于每一个函数和类方法被重新装载,设置old_function .__ code__ = new_function .__ code__。
  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



Answer 4:

你必须得到新鲜的模块新的类并为其分配回实例。

如果你能触发该操作在你使用的实例与此混入:

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)


Answer 5:

下面的代码你想要做什么,但请不要使用它 (至少是在你很确定你做正确的事), 我张贴这只是为了说明目的

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)

当您的代码实例化myObject ,你得到的一个实例ClassChange 。 这种情况下有一个名为实例方法 run 。 该对象保持这种实例方法(通过nosklo解释的原因),即使重装时,因为重装只有重新加载 ClassChange

上面的代码run是一个类的方法 。 类的方法总是难免和类,而不是实例(这就是为什么他们的第一个参数通常被称为操作cls ,而不是self )。 德恩ClassChange被重新加载,所以是这个类的方法。

你可以看到,我还通过实例作为参数与ClassChange的正确的(相同的)情况下工作。 你可以看到,由于相同的对象ID在这两种情况下打印。



Answer 6:

我不知道这是否是做的最好的办法,或者你想要做什么......网格但这可能会为你工作。 如果你想改变一个方法的行为,对于某些类型的所有对象...只需使用一个函数变量。 例如:


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到新的东西。 我尝试了这种代码。 它工作正常:-)。

这是否为您工作?



Answer 7:

有技巧,让你想要什么可能。

有人已经提到,你可以有信守实例列表的类,然后改变类的每个实例,以重新加载时新的。

然而,这是没有效率的。 更好的方法是改变旧的类,以便它是一样的新类。



文章来源: In Python, how do you change an instantiated object after a reload?