带参数的多重继承
Multiple inheritance with arguments
我已经阅读了很多关于继承的内容,但我似乎无法理解为什么这会给我一个错误(使用 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 分钟,来自 PyCon 2015 的 Raymond Hettinger 的 this talk 做得更好 much。具体来说,遍历 "siblings" 的想法可能会产生误导。相反,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)
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
但输出可能会让您感到困惑:为什么要创建 2 个 B 实例? 或 为什么指定参数默认值很重要?
好吧,请考虑以下(同样,稍作修改)代码:
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
object:
c = Log(123, 456)
我们得到:
Before A
A got: 123
B got: None
Before B
B got: 456
Log
这里发生的事情是 super(A, self).__init__()
实际上是在调用 B.__init__()
。
这是因为super()
会在parent找人实现方法之前遍历siblings
在本例中,它找到了 B 的 __init__
方法。 B 的 __init__
方法 then 也查找兄弟然后 parents,但是由于 B 没有兄弟(由 Log
class -- self
是其中的一个实例),B 的 __init__
调用 object.__init__
实际上什么都不做。
换句话说(init
对于 __init__
是 shorthand):
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
会先被检查,然后 parent class.
这不会像您注意到的那样走向另一个方向,因此 B.init()
中的 super
不会 找到 A.init()
,而是找到 object.init()
。同样,这是因为在 Log
的上下文中,B
将在 在 A
之后检查,然后是 parent class, object
.
一些阅读:
编辑: 要解决此问题,您可以显式调用 superclass __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__()
实际上什么都不做,您可以简单地 re-write 您的代码为:
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
我已经阅读了很多关于继承的内容,但我似乎无法理解为什么这会给我一个错误(使用 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 分钟,来自 PyCon 2015 的 Raymond Hettinger 的 this talk 做得更好 much。具体来说,遍历 "siblings" 的想法可能会产生误导。相反,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)
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
但输出可能会让您感到困惑:为什么要创建 2 个 B 实例? 或 为什么指定参数默认值很重要?
好吧,请考虑以下(同样,稍作修改)代码:
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
object:
c = Log(123, 456)
我们得到:
Before A A got: 123 B got: None Before B B got: 456 Log
这里发生的事情是 super(A, self).__init__()
实际上是在调用 B.__init__()
。
这是因为super()
会在parent找人实现方法之前遍历siblings
在本例中,它找到了 B 的 __init__
方法。 B 的 __init__
方法 then 也查找兄弟然后 parents,但是由于 B 没有兄弟(由 Log
class -- self
是其中的一个实例),B 的 __init__
调用 object.__init__
实际上什么都不做。
换句话说(init
对于 __init__
是 shorthand):
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
会先被检查,然后 parent class.
这不会像您注意到的那样走向另一个方向,因此 B.init()
中的 super
不会 找到 A.init()
,而是找到 object.init()
。同样,这是因为在 Log
的上下文中,B
将在 在 A
之后检查,然后是 parent class, object
.
一些阅读:
编辑: 要解决此问题,您可以显式调用 superclass __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__()
实际上什么都不做,您可以简单地 re-write 您的代码为:
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