我已经读了很多关于继承的文章,但我似乎不明白为什么这会给我带来错误(使用Python 2.7.x)
class A(object):
def __init__(self, value):
super(A, self).__init__()
print 'First %s' % value
class B(object):
def __init__(self, value):
super(B, self).__init__()
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)
// Error: __init__() takes exactly 2 arguments (1 given)
# Traceback (most recent call last):
# File "<maya console>", line 21, in <module>
# File "<maya console>", line 13, in __init__
# File "<maya console>", line 3, in __init__
# TypeError: __init__() takes exactly 2 arguments (1 given) //
前言:我在这里解释MRO的尝试非常不足。如果你有45分钟的时间,2015年PyCon的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的实例:
a = A("aa") # A got: aa
b = B("bb") # B got: bb
但是当我们尝试创建Log的实例时,我们得到了一个异常:
c = Log(123,456)
追踪(最近一次通话):文件"temp2.py",第21行,位于c=对数(123456)文件"temp2.py",第13行,在__init中__A.__init__(自我,A)文件"temp2.py",第3行,在__init中__super(A,self)__init__()TypeError:__init__()正好接受2个参数(给定1个)
为了弄清楚这里发生了什么,我们可以默认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得到:无A得了:123B得到:456日志
但输出可能会让您感到困惑:为什么要创建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)
我们得到:
A之前A得了:123B得到:无B之前B得到:456日志
这里发生的是super(A, self).__init__()
实际上正在调用B.__init__()
。
这是因为super()
将在父级查找实现该方法的人之前遍历同级。
在这种情况下,它找到了B的__init__
方法。B的__init__
方法然后也会先查找兄弟姐妹,然后查找父母,但由于B没有兄弟姐妹(由Log
类定义,self
是其实例),因此B的__init__
调用object.__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
找到B.init()
(而不是object.init()
)的原因是首先搜索同级。在self
(Log(A,B)
)的上下文中,B
将在父类之前首先被检查。
这不会像您可能注意到的那样朝另一个方向发展,因此B.init()
中的super
不会找到A.init()
,而是找到object.init()
。同样,这是因为在Log
的上下文中,B
将在A
之后被检查,然后是父类object
。
阅读:
super()
文档- Python的super()被认为是super
- Python 2.3方法解析顺序
EDIT:要解决此问题,可以显式调用超类__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)
两者都将输出您所期望的内容:
前1000第二2222日志