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

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

问题描述

我读了很多关于继承的书,但我似乎无法理解为什么这会给我一个错误(使用 Python 2.7.x).

A 类(对象):def __init__(self, value):super(A, self).__init__()打印第一个 %s"% 值B类(对象):def __init__(self, value):super(B, self).__init__()打印第二个 %s" % 值类日志(A,B):def __init__(self, a, b):A.__init__(self, a)B.__init__(self, b)打印日志"x = 日志(1000, 2222)//错误:__init__() 正好接受 2 个参数(给定 1 个)# 回溯(最近一次调用最后一次):# File "", line 21, in # 文件",第 13 行,在 __init__ 中# 文件",第 3 行,在 __init__ 中# TypeError: __init__() 需要 2 个参数(1 个给定)//

解决方案

前言:我在这里解释 MRO 的尝试非常缺乏.如果您有 45 分钟的时间,PyCon 2015 的 Raymond Hettinger 的本次演讲做了一个好多更好的工作.具体来说,遍历兄弟姐妹"的想法可能会产生误导.相反,super 调用只是跟在 MRO 之后,(参见 help(Log)).

尽管投了反对票,但这实际上是一个好问题.

考虑稍微修改的代码:

A 类(对象):def __init__(self, value):super(A, self).__init__()打印 'A got: %s' % 值B类(对象):def __init__(self, value):super(B, self).__init__()打印 'B got: %s' % 值类日志(A,B):def __init__(self, a, b):A.__init__(self, a)B.__init__(self, b)打印日志"

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

a = A("aa") # A得到:aab = B("bb") # B得到:bb

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

c = Log(123,456)

<前>回溯(最近一次调用最后一次):文件temp2.​​py",第 21 行,在c = 日志(123, 456)文件temp2.​​py",第 13 行,在 __init__ 中A.__init__(self, a)文件temp2.​​py",第 3 行,在 __init__ 中super(A, self).__init__()类型错误:__init__() 正好有 2 个参数(给定 1 个)

为了弄清楚这里发生了什么,我们可以给 value 参数一个默认值(我使用 None):

A 类(对象):def __init__(self, value=None):super(A, self).__init__()打印 'A got: %s' % 值B类(对象):def __init__(self, value=None):super(B, self).__init__()打印 'B got: %s' % 值类日志(A,B):def __init__(self, a, b):A.__init__(self, a)B.__init__(self, b)打印日志"

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

c = Log(123, 456)

<前>B得到:无A得到:123B得到:456日志

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

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

A 类(对象):def __init__(self, value=None):打印 'A got: %s' % 值super(A, self).__init__()B类(对象):def __init__(self, value=None):打印 'B got: %s' % 值super(B, self).__init__()类日志(A,B):def __init__(self, a, b):打印(A之前")A.__init__(self, a)打印(B之前")B.__init__(self, b)打印日志"

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

c = Log(123, 456)

我们得到:

<前>A前A得到:123B得到:无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() -->对象.init()B.init()super(B, self).init() -->对象.init()

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

这不会像你可能注意到的那样走向另一个方向,所以 B.init() 中的 super 不会找到A.init(),而是找到 object.init().同样,这是因为在 Log 的上下文中,B 将被检查 after A,然后是父级类,object.

一些阅读:

<小时>

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

A 类(对象):def __init__(self, value):object.__init__(self)打印第一个 %s"% 值B类(对象):def __init__(self, value):object.__init__(self)打印第二个 %s" % 值类日志(A,B):def __init__(self, a, b):A.__init__(self, a)B.__init__(self, b)打印日志"x = 日志(1000, 2222)

或者,由于 object.__init__() 实际上什么都不做,您可以简单地将代码重写为:

A 类(对象):def __init__(self, value):打印第一个 %s"% 值B类(对象):def __init__(self, value):打印第二个 %s" % 值类日志(A,B):def __init__(self, a, b):A.__init__(self, a)B.__init__(self, b)打印日志"x = 日志(1000, 2222)

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

<前>前 1000第二个2222日志

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

解决方案

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'

We can create instances of A and B without issue:

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

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)

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

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'

Now, when we try to create our c object:

c = Log(123, 456)

We get:

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

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

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

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.

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

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.

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.

Some reading:


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

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天全站免登陆