autoreload和package导致TypeError:super(type,obj):obj必须是类型的实例或子类型

Leo*_*Leo 14 python ipython

我有python代码跨越几个文件,为了方便我打包,最后在my_package目录下有以下3个文件:

__init__.py
Run Code Online (Sandbox Code Playgroud)

内容:

from file1 import *
from file2 import *
Run Code Online (Sandbox Code Playgroud)

file1.py内容:

class Base(object):
    pass
Run Code Online (Sandbox Code Playgroud)

file2.py内容:

from file1 import Base 
class Derived(Base):
    def __init__(self):
        return super(Derived, self).__init__()
Run Code Online (Sandbox Code Playgroud)

然后我在IPython中执行:

>>>%autoreload 2
>>>import my_package
>>>t = my_package.Derived()
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好.但后来我对file2.py进行了更改,比如说添加了一个虚拟属性.现在我执行时:

>>>t = my_package.Derived()
>>>      2 class Derived(Base):
>>>      3     def __init__(self):
>>>----> 4         return super(Derived, self).__init__()
>>>      5 
>>>      6 dumm = 'asdf'
>>>
>>>TypeError: super(type, obj): obj must be an instance or subtype of type
Run Code Online (Sandbox Code Playgroud)

在我重新启动IPython控制台之前,这不会消失.为什么不自动重载正确处理?如果我将Base和Derived放入单个模块文件而不是包中,一切都有效.

Blc*_*ght 11

我不是IPython的用户,所以我不能确切地说出发生了什么,但我想这是from file2 import *在你的__init__.py文件中使用的症状.

当您使用创建Derived类的实例时package.Derived,您没有获得该类的最新版本,而是获取该软件包首次加载并from file2 import *运行该语句时的当前版本.当您修改模块代码并且IPython重新加载它时,它发生了变化package.file2.Derived,但没有变化package.Derived.

但是,该类的旧版本仍然具有对其模块命名空间的引用,并且当它尝试在super调用中按名称查找自身时,它会找到该类的较新版本.这就是你得到错误的原因,因为这两个Derived类不一样.

如果package.file2.Derived直接访问,您可能会避免此问题.这将始终让您进入该类的当前版本,该版本不应该与super调用有任何问题.请注意,如果在修改模块之前仍然存在该类的实例,则可能仍然存在问题(但这可能并不令人惊讶).

  • 这是有道理的.我试图使用"from file2 import Derived"而不是*,但不幸的是,这并没有解决它.奇怪的是,autoreload无法弄清楚package.Derived应该更新. (3认同)
  • @Leo如果B包含`__init __.py`文件,则使用`from AB import C`时似乎发生错误.这太让我烦恼了. (3认同)

amo*_*olk 9

在 Python 3 中,使用 newsuper().__init__()而不是super(Derived, self).__init__()为我解决了问题