python - 有什么方法可以在 IPython 重新加载后手动修复 `super()` 的操作(避免 TypeError)?

标签 python python-2.7 ipython typeerror ipython-magic

这是一个人为的玩具示例,用于触发我遇到的问题:

我有几个类,假设它们在本地文件“issue.py”中:

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 session 中使用它们,可能是这样的:

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 类“花哨的东西”中存在一个错误,并在那里进行了一个小的编辑——甚至可能只是添加了一些日志记录。然后我想重复 save():

In [7]: c.save()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-7-6970514bfc33> in <module>()
----> 1 c.save()

/Users/scratch/Documents/dev2015/gensim_venv/src/gensim-develop/docs/notebooks/scratch~/issue.py in save(self)
      7     def save(self):
      8         # misc stuff
----> 9         super(B, self).save()
     10 
     11 class C(B):

TypeError: super(type, obj): obj must be an instance or subtype of type

哦不!重新加载类后可怕的 TypeError,而较旧的实例保留了一些较旧的父类(super class)! SO 和其他地方对这个问题进行了讨论,但没有明确的恢复方法。

但碰巧的是,我真的非常希望能够在我的旧 c 上运行稍微更新过的 A.save()实例。 (我在内存中有 20GB 以上的数据,花了大约一天半的时间生成这些数据,这些数据将通过父类(super class)方法以首选方式保存。我已经通过其他手动方法保存了足够多的数据,我认为我能够在重新启动的 IPython 内核中重建 c。但是,虽然我仍然拥有真实的对象,但我更愿意对修补后的 A 进行实际测试.save()——甚至可能在完全重启内核之前对其进行更多修复/测试。)

所以我对任何策略或技巧都很感兴趣,无论它们在其他情况下可能多么不明智,都可以将 c 强制到当前类定义中,一直向上,以便 c.save() 正常工作。

有什么想法吗?

我希望适用于这个玩具示例的任何东西都适用于我的真实设置,即基于 CPython 2.7.10 的 IPython)。 (然而,在实际情况中,这三个类在不同的文件中。)

最佳答案

您可以将更新后的类重新分配给您的实例:

from issue import A, B, C
c.__class__ = C

此时 self 将再次成为重新加载的类层次结构的适当实例。请注意,您也需要在这里重新绑定(bind)全局变量;重新加载模块,而不是对类的全局引用。

如果您有一个包含多个模块的更复杂的设置,您需要替换对旧类的所有引用。如果您在以下形式的任何模块中导入:

from some_module import Bar

class Foo(Bar):
    # ...

然后 Bar 不会在 some_module 重新加载时重新绑定(bind)。您可以通过避免绑定(bind)到全局变量来避免强制重新加载和重新绑定(bind)依赖项;只绑定(bind)模块:

import some_module

class Foo(some_module.Bar):
    # ...

之后你只需要重新绑定(bind)模块对象。或者只是手动重新加载所有涉及的模块,毕竟您的数据存在于您的实例中。

演示:

>>> 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()

关于python - 有什么方法可以在 IPython 重新加载后手动修复 `super()` 的操作(避免 TypeError)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31363311/

相关文章:

python - 如何检查代码是否在 IPython 笔记本中执行?

python - 在 Python 中删除一个对象和对它的所有引用?

javascript - 保存修改后的 ColumnDataSource

python - 如何在 Python 中配置日志记录

python - 有没有一种首选方法可以使用 pytest 测试回调?

python - IPython 和单元测试,固定测试不显示为固定

python - Python中不带[]的列表理解

python - 在 Google AppEngine 上使用 upload_data 不允许我使用基于 id 的键更新实体

Python:在 scipy.optimize.newton 中将列表作为参数传递

python - 如何加载现有的 ipython 笔记本?