有没有办法在IPython重新加载后手动修复“super()”的操作(避免TypeError)?
下面是一个人为制造的玩具示例,用于触发我遇到的问题: 我有几个类,假设它们在本地文件'issue.py'中:有没有办法在IPython重新加载后手动修复“super()”的操作(避免TypeError)?,python,python-2.7,ipython,typeerror,ipython-magic,Python,Python 2.7,Ipython,Typeerror,Ipython Magic,下面是一个人为制造的玩具示例,用于触发我遇到的问题: 我有几个类,假设它们在本地文件'issue.py'中: class A(object): def save(self): # fancy stuff pass class B(A):
class A(object):
def save(self):
# fancy stuff
pass
class B(A):
def save(self):
# misc stuff
super(B, self).save()
class C(B):
pass
我在IPython会话中使用它们,可能是这样的:
In [1]: %load_ext autoreload
In [2]: %autoreload 2
In [3]: from issue import A, B, C
In [4]: c = C()
In [5]: c.foo = 'whatever'
In [6]: c.save()
到目前为止,一切顺利。但后来我意识到a类“奇特的东西”中有一个bug,并在那里做了一个小的编辑——甚至可能只是添加了一点日志记录。然后我想重复save()
:
[7]中的:c.save()
---------------------------------------------------------------------------
TypeError回溯(最近一次调用上次)
在()
---->1 c.保存()
/用户/scratch/Documents/dev2015/gensim\u venv/src/gensim develop/docs/notebooks/scratch~/issue.py保存(self)
7 def保存(自):
8#杂项材料
---->9 super(B,self).save()
10
11 C(B)类:
TypeError:super(type,obj):obj必须是类型的实例或子类型
哦,不!。重新加载类后会出现可怕的TypeError
,而较旧的实例会保留一些较旧的超类!SO和其他地方都在讨论这个问题,但没有明确的复苏方案
但是,碰巧的是,我非常非常希望能够在我的旧c
实例上运行稍微更新的A.save()
。(我的内存中有20GB以上的数据,大约需要一天半的时间来生成这些数据,这些数据将通过超类方法以首选方式保存。我已经通过其他手动方法保存了足够多的数据,我认为我能够在重新启动的IPython内核中重建c
。但是,尽管我仍然拥有真实的对象,但我还是要做很多准备。)请尝试对修补后的a.save()
进行实际测试,甚至可能在完全重启内核之前对其进行更多修复/测试。)
因此,我对任何策略或技巧感兴趣,不管它们在其他情况下有多不明智,都可以将c
强制到当前的类定义中,直到c.save()
起作用
有什么想法吗
我希望任何适用于这个玩具示例的东西都能在我的真实设置中工作,这是一个基于CPython 2.7.10的IPython)。(但是,在实际情况中,这三个类位于不同的文件中。)您可以将更新的类重新分配给实例:
from issue import A, B, C
c.__class__ = C
此时,self
将再次成为重新加载的类层次结构的适当实例。注意,您也需要在这里重新绑定globals;模块重新加载,而不是类的全局引用
如果有多个模块的更复杂的设置,则需要替换对旧类的所有引用。如果您在表单的任何模块中有导入:
from some_module import Bar
class Foo(Bar):
# ...
然后,当重新加载某个模块时,Bar
不会被重新绑定。通过避免绑定到全局变量,可以避免强制重新加载和绑定依赖项;改为仅绑定模块:
import some_module
class Foo(some_module.Bar):
# ...
之后,只需重新绑定模块对象。或者只是手动重新加载所有涉及的模块,毕竟您的数据存在于实例中
演示:
>>A类(对象):
... def保存(自我):
... # 花哨的东西
... 通过
...
>>>B(A)类:
... def保存(自我):
... # 杂项材料
... super(B,self).save()
...
>>>丙(乙)类:
... 通过
...
>>>c=c()
>>>c.foo=‘随便什么’
>>>c.保存()
>>>
>>>#重新定义类会破坏实例
...
>>>A类(对象):
... def保存(自我):
... # 花哨的东西
... 通过
...
>>>B(A)类:
... def保存(自我):
... # 杂项材料
... super(B,self).save()
...
>>>丙(乙)类:
... 通过
...
>>>isinstance(c,c)
假的
>>>c.保存()
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
文件“”,第4行,保存
TypeError:super(type,obj):obj必须是类型的实例或子类型
>>>
>>>#通过重新绑定类来解决问题
...
>>>c.。\uuuu类\uuuuu=c
>>>isinstance(c,c)
真的
>>>c.保存()
在Python3中,使用新的super()。\uuuu init\uuuu()
语法而不是super(B,self)。\uuuu init\uuuuu()
为我解决了一个类似的问题。您是否尝试过创建新的C
的新实例,然后用旧的C
中的数据手动覆盖其数据?这将如何或是否起作用取决于c
存储什么样的数据,以及如何存储,但这是我想到的第一种方法。这也是我想到的。但是,即使在玩具示例中,如果尝试创建c2
,它在c2.save()上也会出现相同的错误,因此它的超类层次结构不是固定的。(在我的实际设置中,也有一个类似的类型错误影响C的\uuuuu init\uuuu
方法,它对B的\uuuuu init\uuuuu
执行super()操作。我尝试了C.\uuu class\uuuuu=C
,但随后C.save()
触发了完全相同的错误。@gojomo:那么会话中的C
是否仍然绑定到旧类?然后从问题导入A、B、C中发出另一个。宾果,看起来是这样做的!现在在启发示例的真实会话中尝试……唉,“真实”设置中仍然存在问题。在那里,B和C是独立的文件一个模块;A来自另一个模块。要尝试编辑每个文件(由IPython的更改检测拾取),然后显式导入…@gojomo:您正在使用全局导入;来自mod import classname
;如果您导入模块,则不必重新加载
import some_module
class Foo(some_module.Bar):
# ...
>>> class A(object):
... def save(self):
... # fancy stuff
... pass
...
>>> class B(A):
... def save(self):
... # misc stuff
... super(B, self).save()
...
>>> class C(B):
... pass
...
>>> c = C()
>>> c.foo = 'whatever'
>>> c.save()
>>>
>>> # Re-defining the classes breaks the instance
...
>>> class A(object):
... def save(self):
... # fancy stuff
... pass
...
>>> class B(A):
... def save(self):
... # misc stuff
... super(B, self).save()
...
>>> class C(B):
... pass
...
>>> isinstance(c, C)
False
>>> c.save()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in save
TypeError: super(type, obj): obj must be an instance or subtype of type
>>>
>>> # Fixing the issue by rebinding the class
...
>>> c.__class__ = C
>>> isinstance(c, C)
True
>>> c.save()