要更新一个类的所有实例,有必要在某个地方跟踪这些实例——通常通过弱引用(弱值字典是最方便和通用的),因此“跟踪”功能不会阻止不需要的实例消失,当然!
您通常希望在类对象中保留这样一个容器,但是在这种情况下,由于您将重新加载模块,因此获取旧的类对象并非易事;在模块级别工作更简单。
因此,假设一个“可升级模块”需要在其开始时定义一个弱值 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
模块中的每个类通常都必须有一个注册新实例__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
这还不是全部——关于这个主题还有很多话要说——但是,这是要点,而且答案已经足够长了(我已经筋疲力尽了;-)。