Python 中的多重继承怪癖或错误?

Multiple Inheritance Quirk, or Bug in Python?

我正在写一个 class,它应该是 class,以及其他 classes,code.InteractiveInterpreter。出于某种原因,当您使用多重继承时,class 通常具有的方法之一 (compile) 在其子 class 上不可用。

单继承工作正常:

>>> from code import InteractiveInterpreter
>>> class Single(InteractiveInterpreter): pass
...
>>> hasattr(Single(), 'compile')
True

多重继承没有:

>>> class Double(object, InteractiveInterpreter): pass
...
>>> hasattr(Double(), 'compile')
False

不过颠倒了顺序,它起作用了:

>>> class Flipped(InteractiveInterpreter, object): pass
...
>>> hasattr(Flipped(), 'compile')
True

是否有一些我不知道的多重继承的微妙细节阻止了 compile 在某些情况下被继承,或者 Python 中是否存在导致这种情况的错误(假设有问题的方法的名称也是内置函数的名称,我觉得这可能是可能的。)

我正在尝试使用 InteractiveInterpreter 以外的 class 重现该问题,但我无法...这很好用:

>>> class Original():
...     def compile(self): pass
...
>>> class Secondary(object, Original): pass
...
>>> hasattr(Secondary(), 'compile')
True

我在 Windows 10 上使用 Python 2.7.11,32 位。

您确定Flipped产生了给定的结果吗?

我使用类似的设置得到以下结果,

>>> from code import InteractiveInterpreter
>>> class Flipped(InteractiveInterpreter, object): pass
... 
>>> hasattr(Flipped(), 'compile')
True

基于 source of the modulecompile 不是 class 的方法,而是实例属性 在对象初始化时创建。从 object 继承首先不会提供属性是有道理的,因为它定义了 __init__ 并且 subclass 不调用 InteractiveInterpreter.__init__ 来分配属性。

我不完全知道问题出在哪里,但一种解决方法是显式调用 InteractiveInterpreter 的构造函数,其中实际定义了 compile 方法:

class Double(object, InteractiveInterpreter):
    def __init__(self, *args, **kwargs):
        InteractiveInterpreter.__init__(self, *args, **kwargs)

请注意,仅调用 super(Double, self).__init__(...) 是不够的(至少在我的环境中)。但是,这对我有用

>>> hasattr(Flipped(), 'compile')
True

我的环境: Python 2.7.11(默认,2016 年 1 月 5 日,12:49:55) [GCC 4.2.1 兼容 Apple LLVM 7.0.2 (clang-700.1.81)] darwin

当你混合对象和旧样式时,它看起来像一些东西class

Python 2.7.6 Ubuntu 14.04

我检查了 code.InteractiveInterpreter 的代码(它使用的是旧样式 class),然后进行了一些测试: (我刚刚导入了代码,然后通过 code.__file__ 找到了文件)

class Klass:
    def __init__(self):
        self.compile = 1

class Klass2(object):
    def __init__(self):
        self.compile = 1

class BlankObj:
    pass

class BlankObj2(object):
    pass


class Frist(object, Klass):
    pass

class Second(BlankObj, Klass):
    pass

class Thrid(BlankObj, Klass2):
    pass

class Fourth(BlankObj, Klass2):
    pass

然后摆弄那些东西:

>>> from testing import Frist, Second, Thrid, Fourth
>>> hasattr(Frist(), 'compile')
False
>>> hasattr(Second(), 'compile')
True
>>> hasattr(Thrid(), 'compile')
True
>>> hasattr(Fourth(), 'compile')
True
>>> 
>>> f = Frist()
>>> dir(f)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
>>> s = Second()
>>> dir(s)
['__doc__', '__init__', '__module__', 'compile']

试图创建一个 'Fifth' class,我从对象和 Klass2 继承的地方导致此回溯:

>>> class Fifth(object, Klass2):
...     pass
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Error when calling the metaclass bases
    Cannot create a consistent method resolution
order (MRO) for bases object, Klass2

我不确定为什么会这样,但这肯定与直接将对象与旧样式混合有关class。