假设您有一个从模块内的类实例化的对象。 现在,您重新加载该模块。 接下来你要做的就是让重新加载影响那个类。
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()方法对对象内部的数据进行操作,因此静态模块函数不会这样做...
答案 0 :(得分: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
模块中的每个类必须通常在__init__
中调用_register(self)
来注册新实例。
现在,“reload function”可以通过在重新加载模块之前获取_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
这不是全部 - 关于这个问题还有很多话要说 - 但是,这是要点,答案已经足够长了(而且,我已经筋疲力尽了; - )。
答案 1 :(得分:6)
你必须制作一个新对象。没有办法神奇地更新现有对象。
阅读reload
builtin documentation - 非常清楚。这是最后一段:
如果模块实例化类的实例,则重新加载定义类的模块不会影响实例的方法定义 - 它们继续使用旧的类定义。派生类也是如此。
文档中还有其他注意事项,所以你真的应该阅读它,并考虑替代方案。也许你想用为什么开始一个新的问题,你想要使用reload
并要求其他方法来实现同样的目标。
答案 2 :(得分:4)
我的方法如下:
这种方法的优点是:
您可以在此处阅读有关技术(及其局限性)的信息: http://luke-campagnola.blogspot.com/2010/12/easy-automated-reloading-in-python.html
您可以在此处下载代码: http://luke.campagnola.me/code/downloads/reload.py
答案 3 :(得分:3)
您必须从新模块中获取新类并将其分配回实例。
如果您可以在使用此mixin的实例时随时触发此操作:
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)
答案 4 :(得分:2)
以下代码可以满足你的需求,但是请不要使用它(至少在你确定自己正在做正确的事情之前),我是发布仅供参考。
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
)。重新加载了Wenn ClassChange
,这个类方法也是如此。
您可以看到我还将实例作为参数传递,以使用ClassChange的正确(相同)实例。您可以看到,因为在两种情况下都会打印相同的对象ID。
答案 5 :(得分:1)
我不确定这是否是最好的方法,或者与你想做的事情相符...但这可能对你有用。如果要更改某个方法的行为,对于某种类型的所有对象...只需使用一个函数变量。例如:
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
设置为新的。我试过这段代码。它工作正常: - )。
这对你有用吗?
答案 6 :(得分:1)
有一些技巧可以让你想要的东西成为可能。
有人已经提到过,你可以拥有一个保存其实例列表的类,然后在重新加载时将每个实例的类更改为新实例。
然而,这并不高效。更好的方法是更改旧类,使其与新类相同。