如果没有__init__方法定义一个类会怎么样?

时间:2018-04-23 12:07:04

标签: python multiple-inheritance method-resolution-order

假设我已经定义了以下四个类:

(代码已经在Python 3.6.5上进行了测试。但是,我预计也应该适用于Python 2.7.x from __future__ import print_function)< / p>

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 信息。

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

    但是,以下结果让我非常困惑!

    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之间的行为不同?

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

    EDIT2 我很傻,这是关于mro。

    感谢您的帮助。

2 个答案:

答案 0 :(得分:4)

  1. 班级A没有单独的__init__方法 - 通过A自己的mro查找。

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

    >>> B.__init__ is object.__init__
    False
    
  3. 请注意,区别在于继承 __init__。仅仅因为可以提供A.__init__并不意味着A本身就有__init__方法。

    1. 当C查找其__init__时,会尝试以下操作:

      • C.__init__不存在
      • A.__init__不存在
      • object.__init__存在并被称为
    2. 当D查找其__init__时,会尝试以下操作:

      • D.__init__不存在
      • A.__init__不存在
      • B.__init__存在并被称为
      • object.__init__永远不会被抬头
    3. 这种差异是使用super而不是显式基类的重点。它允许将专门的类插入层次结构(example)。

答案 1 :(得分:2)

此行为的原因是属性查找在python中的工作方式。当您访问A.__init__时,python会在内部遍历A的MRO,直到找到定义 __init__属性的类。 在此查找过程中会忽略继承的属性。

当您在super(C, self).__init__(value)中调用C.__init__时,python会遍历MRO [A, object],直到找到__init__属性。重要的是A 定义__init__属性,因此查找超过Aobject并返回object.__init__ }。

同样的事情发生在D.__init__,除了在这种情况下,被遍历的MRO是[A, B, object]。同样,A没有定义__init__,因此继续查找并返回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