在Python中混合super和经典调用
首先,让我引用一下《专家Python编程》这本书中的一段话:
在下面的例子中,一个C类通过__init__方法调用它的父类,这样会导致B类被调用两次!
class A(object):
def __init__(self):
print "A"
super(A, self).__init__()
class B(object):
def __init__(self):
print "B"
super(B, self).__init__()
class C(A,B):
def __init__(self):
print "C"
A.__init__(self)
B.__init__(self)
print "MRO:", [x.__name__ for x in C.__mro__] #prints MRO: ['C', 'A', 'B', 'object']
C() #prints C A B B
最后,这里有个解释,告诉我们发生了什么:
这是因为调用了A.__init__(self),这个调用是通过C类的实例进行的,因此会导致super(A, self).__init__()调用B的构造函数。 换句话说,super应该在整个类的层级中使用。问题是,有时候这个层级的一部分是在第三方代码中。
我不明白为什么"super(A, self).__init__()
会调用B的构造函数"。请解释一下这个过程。非常感谢。
2 个回答
要理解这个行为,你需要知道 super
不是直接调用基类,而是沿着 __mro__
的顺序查找下一个匹配的方法。所以,调用 super(A, self).__init__()
时,它查看 __mro__ == ['C', 'A', 'B', 'object']
,发现 B
是下一个有匹配方法的类,然后调用 B
的方法(构造函数)。
如果你把 C
改成
class C(A,B):
def __init__(self):
print "C1"
A.__init__(self)
print "C2"
B.__init__(self)
print "C3"
你会得到
MRO: ['C', 'A', 'B', 'object']
C1
A
B
C2
B
C3
这展示了 A
的构造函数是如何调用 B
的。
关于 super
的文档说明是这样的:
它返回一个代理对象,这个对象会把方法调用转发给父类或兄弟类。这在访问被重写的方法时非常有用。搜索的顺序和
getattr()
一样,只是类型本身会被跳过。
当你在 C
类中执行 A.__init__(self)
时,super(A, self)
会返回 <super: <class 'A'>, <C object>>
。因为实例是 C
(<C object>
),所以会找到 C
继承层次中的所有类,并对它们调用 __init__
方法。因此,你会看到 'B' 被调用了两次。
为了验证这一点,可以再添加一个类 'Z',让 'C' 也继承自 'Z'。看看会发生什么。
class Z(object):
def __init__(self):
print "Z"
super(Z, self).__init__()
class C(A, B, Z):
def __init__(self):
print "C"
A.__init__(self)
B.__init__(self)
Z.__init__(self)
在这种情况下,A
会调用 B
和 Z
。而 B
也会调用 Z
。