前言:我在这里解释MRO的尝试是相当不足的。如果你有45分钟的时间,this talk来自PyCon 2015的Raymond Hettinger会做一个更好的工作。具体来说,遍历“兄弟姐妹”的想法可能会产生误导。相反,super调用只需遵循MRO(请参见help(Log))。在
尽管投了反对票,但这实际上是个好问题。在
考虑稍微修改的代码:class A(object):
def __init__(self, value):
super(A, self).__init__()
print 'A got: %s' % value
class B(object):
def __init__(self, value):
super(B, self).__init__()
print 'B got: %s' % value
class Log(A, B):
def __init__(self, a, b):
A.__init__(self, a)
B.__init__(self, b)
print 'Log'
我们可以创建A和B的实例而不会出现问题:
^{pr2}$
但是,当我们尝试创建一个Log实例时,会出现一个异常:c = Log(123,456)Traceback (most recent call last):
File "temp2.py", line 21, in
c = Log(123, 456)
File "temp2.py", line 13, in __init__
A.__init__(self, a)
File "temp2.py", line 3, in __init__
super(A, self).__init__()
TypeError: __init__() takes exactly 2 arguments (1 given)
为了弄清楚这里发生了什么,我们可以给value参数一个默认值(我使用None):class A(object):
def __init__(self, value=None):
super(A, self).__init__()
print 'A got: %s' % value
class B(object):
def __init__(self, value=None):
super(B, self).__init__()
print 'B got: %s' % value
class Log(A, B):
def __init__(self, a, b):
A.__init__(self, a)
B.__init__(self, b)
print 'Log'
现在我们同样的代码可以正常运行:c = Log(123, 456)B got: None
A got: 123
B got: 456
Log
但是输出结果可能会让您困惑:为什么要创建2b实例?或为什么指定参数默认值很重要?
好吧,考虑一下以下代码(同样,稍作修改):class A(object):
def __init__(self, value=None):
print 'A got: %s' % value
super(A, self).__init__()
class B(object):
def __init__(self, value=None):
print 'B got: %s' % value
super(B, self).__init__()
class Log(A, B):
def __init__(self, a, b):
print("Before A")
A.__init__(self, a)
print("Before B")
B.__init__(self, b)
print 'Log'
现在,当我们尝试创建c对象时:c = Log(123, 456)
我们得到:Before A
A got: 123
B got: None
Before B
B got: 456
Log
这里发生的是super(A, self).__init__()实际上在调用{}。在
这是因为super()将在父对象寻找实现该方法的人之前遍历同级。在
在本例中,它找到B的__init__方法。B的__init__方法then也查找同级,然后是父级,但是由于B没有同级(由Log类定义,该类是self的一个实例),因此B的__init__调用{},实际上什么也不做。在
换句话说(init是__init__的简写):Log.init()
A.init()
super(A, self).init() > B.init()
super(B, self).init() > object.init()
B.init()
super(B, self).init() > object.init()
A.init()中的super找到{}(而不是{})的原因是首先搜索同级。在self(Log(A,B))的上下文中,B将首先在父类之前进行检查。在
这不会像您所注意到的那样,因此super内部的B.init()不会找到A.init(),而是找到{}。同样,这是因为在Log的上下文中,B将在A之后检查,然后是父类object。在
一些阅读:
编辑:要解决此问题,可以显式调用超类__init__,而不是依赖super():class A(object):
def __init__(self, value):
object.__init__(self)
print 'First %s' % value
class B(object):
def __init__(self, value):
object.__init__(self)
print 'Second %s' % value
class Log(A, B):
def __init__(self, a, b):
A.__init__(self, a)
B.__init__(self, b)
print 'Log'
x = Log(1000, 2222)
或者,由于object.__init__()实际上什么也不做,所以您可以简单地将代码重新编写为:class A(object):
def __init__(self, value):
print 'First %s' % value
class B(object):
def __init__(self, value):
print 'Second %s' % value
class Log(A, B):
def __init__(self, a, b):
A.__init__(self, a)
B.__init__(self, b)
print 'Log'
x = Log(1000, 2222)
这两种方法都会产生你预期的结果:First 1000
Second 2222
Log