带参数的多重继承 [英] Multiple inheritance with arguments

查看:77
本文介绍了带参数的多重继承的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经阅读了很多有关继承的文章,但是我似乎无法理解为什么这会给我一个错误(使用Python 2.7.x).

I have been reading quite a bit about inheritance, but I can't seem to grasp why this gives me an error (using 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的此演讲会执行 很多 更好的工作.具体来说,遍历兄弟姐妹"的想法可能会产生误导.取而代之的是,super调用仅遵循MRO(请参见help(Log)).

Preface: my attempt to explain the MRO here is pretty deficient. If you have 45 minutes, this talk by Raymond Hettinger from PyCon 2015 does a much better job. Specifically, the idea of traversing "siblings", may be misleading. Instead, the super calls simply follow the MRO, (see help(Log)).

尽管投票不足,但这实际上是一个好问题.

Despite the downvotes, this is actually a good question.

考虑经过稍微修改的代码:

Consider the slightly modified code:

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的实例:

We can create instances of A and B without issue:

a = A("aa")  # A got: aa
b = B("bb")  # B got: bb

但是当我们尝试创建Log的实例时,我们会得到一个异常:

But when we try to create an instance of Log, we get an exception:

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):

To try to figure out what's going on here, we can give a default to the value parameters (I use 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'

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

Now our same code runs without error:

c = Log(123, 456)


B got: None
A got: 123
B got: 456
Log

但是输出结果可能会使您感到困惑:为什么创建2个B实例?为什么指定参数默认值很重要?

But the output might confuse you: Why were 2 B instances created? or Why did specifying parameter defaults matter?

好吧,请考虑以下代码(再次稍作修改):

Well, consider the following (again, slightly modified) code:

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对象时:

Now, when we try to create our 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__().

What's happening here is that super(A, self).__init__() is actually calling B.__init__().

这是因为super()会在父级寻找要实现该方法的父级之前遍历兄弟姐妹.

This is because super() will traverse siblings before the parent looking for someone to implement the method.

在这种情况下,它找到B的__init__方法. B的__init__方法 then 也会先寻找兄弟姐妹,然后再寻找父母,但是由于B没有兄弟姐妹(由Log类定义,而self是它的一个实例),B的__init__调用object.__init__实际上没有任何作用.

In this case, it find's B's __init__ method. B's __init__ method then also looks for siblings then parents, but since there are no siblings for B (as defined by the Log class -- which self is an instance of), B's __init__ calls object.__init__ which effectively does nothing.

采用另一种方式(init__init__的简写):

Put another way (init being shorthand for __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()的原因是因为首先搜索了同级.在self(Log(A,B))的上下文中,将检查B首先,在父类之前.

The reason the super inside A.init() finds B.init() (and not object.init() is because siblings are searched first. And in the context of self (Log(A,B)), B will be checked first, before the parent class.

这不会像您注意到的那样朝另一个方向移动,因此B.init() 中的super不会找到,而是找到object.init().同样,这是因为在Log的上下文中,B将在之后 A进行检查,然后是父类object.

This won't go the other direction as you might notice, so the super inside B.init() won't find A.init(), and instead finds object.init(). Again, this is because in the context of Log, B will be checked after A, followed by the parent class, object.

一些读物:

  • super() docs
  • Python's super() considered super!
  • Python 2.3 Method Resolution Order

要解决此问题,您可以显式调用超类__init__,而不是依赖于super():

To fix this, you could call the superclass __init__ explicitly, instead of relying on 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__()实际上什么也不做,因此您可以简单地将代码重新编写为:

Or, since object.__init__() effectively does nothing, you're able to simply re-write your code as:

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)

这两者都会输出您期望的(我认为):

Both of which will output what (I think) you expected:


First 1000
Second 2222
Log

这篇关于带参数的多重继承的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆