带参数的多重继承

时间:2015-03-27 22:56:37

标签: python python-2.7 arguments multiple-inheritance

我一直在阅读有关继承的内容,但我似乎无法理解为什么这会给我一个错误(使用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) //

1 个答案:

答案 0 :(得分:5)

前言:我在这里解释MRO的尝试非常不足。如果你有45分钟,来自PyCon 2015的Raymond Hettinger this talk做了 很多 更好的工作。具体而言,遍历“兄弟姐妹”的想法可能会产生误导。相反,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对象时:

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()将在父母寻找某人实施该方法之前遍历兄弟姐妹。

在这种情况下,它找到了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()

super A.init()B.init()找到object.init()(而不是self的原因是因为首先搜索了兄弟姐妹。并且在Log(A,B)B { {1}}),将在父类之前检查super

这不会像您可能注意到的那样朝着另一个方向发展,因此B.init() 内的A.init()找不到object.init(),而是找到Log {1}}。同样,这是因为在B的上下文中,{/ 1}}将在 A之后检查,然后是父类object。< / p>

一些阅读:


编辑:要解决此问题,您可以明确调用超类__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