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

use*_*975 14 python module reload python-import

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

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
Run Code Online (Sandbox Code Playgroud)

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

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

Ale*_*lli 15

要更新类的所有实例,有必要跟踪这些实例的某些地方 - 通常通过弱引用(弱值dict是最方便和通用的),因此"保持跟踪"功能不会阻止不需要的实例消失,当然!

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

因此,假设"可升级模块"需要在其开始时定义一个弱值dict(以及一个辅助的"使用下一个键"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
Run Code Online (Sandbox Code Playgroud)

模块中的每个类通常都必须具有注册新实例__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)
Run Code Online (Sandbox Code Playgroud)

唉,人们通常希望给新类提供一个机会,将旧类的对象更好地升级到自身,例如通过合适的类方法.这也不是太难:

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)
Run Code Online (Sandbox Code Playgroud)

例如,假设新版本的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
Run Code Online (Sandbox Code Playgroud)

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


nos*_*klo 6

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

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

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

文档中还有其他注意事项,所以你真的应该阅读它,并考虑替代方案.也许你想开始一个新的问题,为什么你想使用reload并要求其他方法来实现同样的事情.