在Python中,如何在重载后更改实例化对象?
假设你有一个对象,它是从一个模块里的类创建出来的。现在,你重新加载了那个模块。接下来,你想让这个重新加载的操作影响到那个类。
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()方法能操作对象内部的数据,所以静态模块函数就不合适了……
8 个回答
我处理这个问题的方法如下:
- 查看所有导入的模块,只重新加载那些有新 .py 文件的模块(和现有的 .pyc 文件相比)
- 对于每个重新加载的函数和类方法,把旧的函数代码替换成新的函数代码。
- 对于每个重新加载的类,使用 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
你需要创建一个新的对象。没有办法神奇地更新已经存在的对象。
看看reload
这个内置函数的文档,里面讲得很清楚。这里是最后一段:
如果一个模块创建了一个类的实例,重新加载定义这个类的模块并不会影响这些实例的方法定义——它们仍然使用旧的类定义。派生类也是如此。
文档中还有其他注意事项,所以你真的应该仔细阅读一下,并考虑其他的选择。也许你可以重新提问,说明你为什么想使用reload
,并询问其他实现同样效果的方法。
要更新一个类的所有实例,首先需要在某个地方记录这些实例的信息。通常我们会用弱引用(weak references),比如使用一个弱值字典,这样可以确保在不需要的时候,这些实例不会被保留。
一般来说,我们会把这个记录的容器放在类对象里,但在这种情况下,由于你会重新加载模块,获取旧的类对象就变得不那么简单了;所以在模块层面上处理会更方便。
假设一个“可升级的模块”需要在开始时定义一个弱值字典(以及一个辅助的“下一个使用的键”的整数),我们可以给它起一些常规的名字:
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
这还不是全部——关于这个主题还有很多要说的——但这就是大致的意思,答案已经够长了(我也累得够呛;-)。