带参数的多重继承

arv*_*urs 3 python arguments multiple-inheritance python-2.7

我已经阅读了很多关于继承的内容,但我似乎无法理解为什么这会给我一个错误(使用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) //
Run Code Online (Sandbox Code Playgroud)

jed*_*rds 5

前言:我在这里解释MRO的尝试非常不足.如果你有45分钟,来自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'
Run Code Online (Sandbox Code Playgroud)

我们可以毫无问题地创建A和B的实例:

a = A("aa")  # A got: aa
b = B("bb")  # B got: bb
Run Code Online (Sandbox Code Playgroud)

但是当我们尝试创建Log实例时,我们得到一个例外:

c = Log(123,456)
Run Code Online (Sandbox Code Playgroud)
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'
Run Code Online (Sandbox Code Playgroud)

现在我们的相同代码运行没有错误:

c = Log(123, 456)
Run Code Online (Sandbox Code Playgroud)
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'
Run Code Online (Sandbox Code Playgroud)

现在,当我们尝试创建我们的c对象时:

c = Log(123, 456)
Run Code Online (Sandbox Code Playgroud)

我们得到:

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()
Run Code Online (Sandbox Code Playgroud)

super内部A.init()发现的原因B.init()(而不是object.init()因为首先搜索兄弟姐妹.在self(Log(A,B))的上下文中,B将首先在父类之前检查.

这不会像你可能注意到的那样朝着另一个方向发展,所以super内部B.init() 找不到A.init(),而是找到object.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)
Run Code Online (Sandbox Code Playgroud)

或者,由于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)
Run Code Online (Sandbox Code Playgroud)

两者都会输出你所期望的(我认为):

First 1000
Second 2222
Log