如果定义一个没有 __init__ 方法的 class 会怎样?

What happens if define a class without __init__ method?

假设我定义了四个类如下:

(该代码已在 Python 3.6.5 上进行了 测试。不过,我预计它 也应该适用于 Python 2.7.xfrom __future__ import print_function)

In [1]: class A(object):
   ...:     pass
   ...: 
   ...: class B(object):
   ...:     def __init__(self, value):
   ...:         print('B(value=%s)' % value)
   ...: 
   ...: class C(A):
   ...:     def __init__(self, value):
   ...:         print('C(value=%s)' % value)
   ...:         super(C, self).__init__(value)
   ...: 
   ...: class D(A, B):
   ...:     def __init__(self, value):
   ...:         print('D(value=%s)' % value)
   ...:         super(D, self).__init__(value)
   ...:         

In [2]: C.mro()
Out[2]: [__main__.C, __main__.A, object]

In [3]: D.mro()
Out[3]: [__main__.D, __main__.A, __main__.B, object]

注意两点:

  1. class A 没有 __init__ 方法;

  2. 根据mro,C和D都有相同的继任者A 信息。

所以我想super(C, self).__init__(value)super(D, self).__init__(value)都会触发A中定义的__init__方法。

然而,下面的结果让我很困惑!

In [4]: C(0)
C(value=0)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-4-75d9b7f7d447> in <module>()
----> 1 C(0)

<ipython-input-1-5252938615c6> in __init__(self, value)
      9     def __init__(self, value):
     10         print('C(value=%s)' % value)
---> 11         super(C, self).__init__(value)
     12 
     13 class D(A, B):

TypeError: object.__init__() takes no parameters

In [5]: D(1)
D(value=1)
B(value=1)
Out[5]: <__main__.D at 0x2a6e8755b70

可以看到class D初始化成功,class C初始化失败

是什么导致 class Cclass D 之间的不同行为?

EDIT 我不认为我的问题是关于 mro(实际上我或多或少知道 mro 关于 python),我的问题是关于不同的__init___ 方法的行为。

EDIT2我傻了,是关于mro的。

感谢您的帮助。

  1. Class A 没有单独的 __init__ 方法 - 它是通过 A 自己的 mro.

    >>> A.__init__ is object.__init__
    True
    
  2. Class B 有一个单独的 __init__ 方法 - 它不需要遍历 mro.

    >>> B.__init__ is object.__init__
    False
    

注意区别是拥有继承__init__。仅仅因为可以提供 A.__init__ 并不意味着 A 本身就有一个 __init__ 方法。

  1. 当 C 查找其 super().__init__ 时,将尝试以下操作:

    • C.__init__ 被跳过
    • A.__init__不存在
    • object.__init__存在并被调用
  2. 当 D 查找其 super().__init__ 时,将尝试以下操作:

    • D.__init__ 被跳过
    • A.__init__不存在
    • B.__init__存在并被调用
    • object.__init__ 从未被查找过

这个区别是使用 super 而不是显式基础 class 的一个主要点。它允许将专门的 classes 插入层次结构 (example)。

此行为的原因是 python 中属性查找的工作方式。当您访问 A.__init__ 时,python 在内部遍历 A 的 MRO,直到找到 定义 和 [=14= 的 class ] 属性。 在此查找期间忽略继承的属性。

当您在 C.__init__ 中调用 super(C, self).__init__(value) 时,python 遍历 MRO [A, object] 直到找到 __init__ 属性。重要的是 A 确实 而不是 定义了 __init__ 属性,因此查找从 Aobject 和 returns object.__init__.

同样的事情发生在D.__init__,除了在这种情况下被遍历的MRO是[A, B, object]。同样,A 没有定义 __init__,因此查找继续并且 returns B.__init__.


作为实验,您可以更改 A 的定义以定义 __init__,如下所示:

class A(object):
    __init__ = object.__init__

你会注意到实例化 D 现在会抛出与 C:

相同的错误
>>> D(3)
D(value=3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "untitled.py", line 17, in __init__
    super(D, self).__init__(value)
TypeError: object.__init__() takes no parameters