假设我已经定义了以下四个类:
(代码已经在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]
注意两件事:
class A
没有__init__
方法;
根据mro,C和D都有相同的后继A
信息。
所以我认为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 C
和class D
之间的行为不同?
编辑我不认为我的问题是关于mro(实际上我或多或少知道关于python的mro),我的问题是关于__init___
方法的不同行为。
EDIT2 我很傻,这是关于mro。
感谢您的帮助。
答案 0 :(得分:4)
班级A
没有单独的__init__
方法 - 通过A
自己的mro
查找。
>>> A.__init__ is object.__init__
True
B类有一个单独的__init__
方法 - 它不需要遍历mro
。
>>> B.__init__ is object.__init__
False
请注意,区别在于有和继承 __init__
。仅仅因为可以提供A.__init__
并不意味着A
本身就有__init__
方法。
当C查找其__init__
时,会尝试以下操作:
C.__init__
不存在A.__init__
不存在object.__init__
存在并被称为当D查找其__init__
时,会尝试以下操作:
D.__init__
不存在A.__init__
不存在B.__init__
存在并被称为object.__init__
永远不会被抬头这种差异是使用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__
属性,因此查找超过A
到object
并返回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