要更新类的所有实例,有必要跟踪这些实例的某个地方——通常是通过弱引用(弱值dict是最简单和通用的),这样“跟踪”功能当然不会阻止不需要的实例离开!
您通常希望在类对象中保留这样一个容器,但是,在本例中,由于您将重新加载模块,因此获取旧的类对象并不容易;在模块级工作更简单。
所以,假设一个“可升级模块”在开始时需要定义一个弱值dict(和一个辅助的“next key to use”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__中,以注册新实例。
现在“reload函数”可以通过在重新加载模块之前获取_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
这并不是全部——关于这个问题还有很多话要说——但是,这是要点,答案已经够长了(而我,已经够累了;—)。