【问题标题】:Multiple inheritance with arguments带参数的多重继承
【发布时间】:2015-06-01 10:04:38
【问题描述】:

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

【问题讨论】:

    标签: python python-2.7 arguments multiple-inheritance


    【解决方案1】:

    前言:我在这里解释 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)
    
    回溯(最近一次通话最后): 文件“temp2.​​py”,第 21 行,在 c = 对数 (123, 456) __init__ 中的文件“temp2.​​py”,第 13 行 A.__init__(self, a) __init__ 中的文件“temp2.​​py”,第 3 行 超级(A,自我).__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得到:无 获得:123 B得到:456 日志

    但输出可能会让您感到困惑:为什么要创建 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)
    

    我们得到:

    A之前 获得:123 B得到:无 B之前 B得到:456 日志

    这里发生的事情是 super(A, self).__init__() 实际上正在调用 B.__init__()

    这是因为super()会先遍历兄弟姐妹,然后再找人实现方法。

    在这种情况下,它找到了 B 的 __init__ 方法。 B 的 __init__ 方法 then 还寻找兄弟姐妹然后是父母,但由于 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()
    

    superA.init() 中找到B.init()(而不是object.init())的原因是因为首先搜索同级。在self (Log(A,B)) 的上下文中,将检查B首先,在父类之前。

    这不会像您可能注意到的那样走向另一个方向,因此B.init() 中的super 不会找到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)
    

    或者,由于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 日志

    【讨论】:

    • 很好的解释!谢谢
    • 我希望我能早点找到这个!感谢您清晰而简单的解释。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-25
    • 2012-12-07
    • 2014-01-07
    • 2012-11-17
    • 1970-01-01
    • 2014-05-15
    相关资源
    最近更新 更多