带参数的多重继承



我已经读了很多关于继承的文章,但我似乎不明白为什么这会给我带来错误(使用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())的原因是首先搜索同级。在selfLog(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日志

最新更新