4

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

我有几个类,假设它们在本地文件'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 会话中使用它们,可能是这样的:

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,而旧实例保留了一些旧的超类!SO 和其他地方对此问题进行了讨论,但没有明确的恢复方法。

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

因此,我对任何策略或技巧感兴趣,无论它们在其他情况下多么不明智,都可以强制c进入当前的类定义,一直向上,这样c.save()就可以了。

有任何想法吗?

我希望任何适用于这个玩具示例的东西都可以在我的真实设置中使用,这是一个基于 CPython 2.7.10 的 IPython)。(但是,在实际情况下,这三个类位于不同的文件中。)

4

2 回答 2

2

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

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

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

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

from some_module import Bar

class Foo(Bar):
    # ...

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

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()
于 2015-07-12T00:56:23.570 回答
1

在 Python 3 中,使用新super().__init__()语法而不是super(B, self).__init__()为我解决了类似的问题

于 2018-10-19T19:03:57.153 回答