Python多重继承:对所有对象调用超级

Python多重继承:对所有对象调用超级,python,Python,我有以下两个超类: class Parent1(object): def on_start(self): print('do something') class Parent2(object): def on_start(self): print('do something else') 我希望有一个继承自两个类的子类能够为双亲调用super class Child(Parent1, Parent2): def on_start(sel

我有以下两个超类:

class Parent1(object):
    def on_start(self):
        print('do something')

class Parent2(object):
    def on_start(self):
        print('do something else')
我希望有一个继承自两个类的子类能够为双亲调用super

class Child(Parent1, Parent2):
    def on_start(self):
        # super call on both parents

蟒蛇式的方法是什么?谢谢。

在您的情况下,由于两个父级都实现了相同的方法,
super
将从左到右与继承的第一个父级相同(对于您的代码,
Parent1
)。使用调用两个函数是不可能的。要执行您想要的操作,您只需从父类调用该方法,如下所示:

class Child(Parent1, Parent2):
    def on_start (self):
        Parent1.on_start()
        Parent2.on_start()
或没有超级:

class Child(Parent1, Parent2):
    def on_start(self):
        Parent1.on_start(self)
        Parent2.on_start(self)
执行摘要:

仅基于类层次结构的属性执行一个方法。如果希望以相同的名称执行多个方法,则需要编写父类以协同执行(通过隐式或显式调用
super
),或者需要循环子类的值

super的工作是将方法调用的一部分或全部委托给类祖先树中的某个现有方法。委托可能在您控制的类之外进行。委派的方法名称需要存在于基类组中

下面介绍的使用
\uuuu base\uuu
try/except
的方法最接近于对您的问题的完整答案,即如何使用相同名称调用每个家长的方法


super
在您想调用父方法之一,但不知道是哪个父方法的情况下非常有用:

class Parent1(object):
    pass

class Parent2(object):
    # if Parent 2 had on_start - it would be called instead 
    # because Parent 2 is left of Parent 3 in definition of Child class
    pass

class Parent3(object):
    def on_start(self):
        print('the ONLY class that has on_start')        

class Child(Parent1, Parent2, Parent3):
    def on_start(self):
        super(Child, self).on_start()
在这种情况下,
Child
有三个直系父母。只有一个Parent3具有on\u start方法。调用
super
可以解决只有
Parent3
在启动时才有
,这就是被调用的方法

如果
Child
继承自多个具有启动
方法的类,则顺序从左到右(如类定义中所列)和从下到上(如逻辑继承)只调用其中一个方法,类层次结构中同名的其他方法已被替换。

因此,更常见的情况是:

class GreatGrandParent(object):
    pass

class GrandParent(GreatGrandParent):
    def on_start(self):
        print('the ONLY class that has on_start')

class Parent(GrandParent):
    # if Parent had on_start, it would be used instead
    pass        

class Child(Parent):
    def on_start(self):
        super(Child, self).on_start()

如果要按方法名称调用多个父方法,在这种情况下,可以使用
\uuuuu base\uuuu
而不是super,并在不知道名称的情况下迭代
子类的基类:

class Parent1(object):
    def on_start(self):
        print('do something')

class Parent2(object):
    def on_start(self):
        print('do something else')

class Child(Parent1, Parent2):
    def on_start(self):
        for base in Child.__bases__:
            base.on_start(self)

>>> Child().on_start()
do something
do something else
如果有一个基类可能在启动时没有
,您可以使用
try/except:

class Parent1(object):
    def on_start(self):
        print('do something')

class Parent2(object):
    def on_start(self):
        print('do something else')

class Parent3(object):
    pass        

class Child(Parent1, Parent2, Parent3):
    def on_start(self):
        for base in Child.__bases__:
            try:
                base.on_start(self)
            except AttributeError:
                # handle that one of those does not have that method
                print('"{}" does not have an "on_start"'.format(base.__name__))

>>> Child().on_start()
do something
do something else
"Parent3" does not have an "on_start"
使用
\uuuuuuuuuu基
的行为类似于
super
,但适用于
子定义中定义的每个类层次结构。也就是说,它将遍历每个forbearer类,直到该类的每个父类在开始时满足
一次

class GGP1(object):
    def on_start(self):
        print('GGP1 do something')

class GP1(GGP1):
    def on_start(self):
        print('GP1 do something else')

class Parent1(GP1):
    pass        

class GGP2(object):
    def on_start(self):
        print('GGP2 do something')

class GP2(GGP2):
    pass

class Parent2(GP2):
    pass            

class Child(Parent1, Parent2):
    def on_start(self):
        for base in Child.__bases__:
            try:
                base.on_start(self)
            except AttributeError:
                # handle that one of those does not have that method
                print('"{}" does not have an "on_start"'.format(base.__name__))

>>> Child().on_start()
GP1 do something else
GGP2 do something
# Note that 'GGP1 do something' is NOT printed since on_start was satisfied by 
# a descendant class L to R, bottom to top
现在想象一个更复杂的继承结构:

如果您希望每个forbearer的
on\u start
方法,您可以使用
\uuuu mro\uuuu
并筛选出那些在该类的
\uu dict\uuu
中没有
on\u start
的类。否则,您可能会获得forbearer的启动
方法。换句话说,
hassattr(c,'on_start')
对于
Child
是其后代的每个类来说都是
True
(本例中
object
),因为
Ghengis
具有
on_start
属性,并且所有类都是Ghengis的后代类

**警告--仅演示**

class Ghengis(object):
    def on_start(self):
        print('Khan -- father to all')    

class GGP1(Ghengis):
    def on_start(self):
        print('GGP1 do something')

class GP1(GGP1):
    pass

class Parent1(GP1):
    pass        

class GGP2(Ghengis):
    pass

class GP2(GGP2):
    pass

class Parent2(GP2):
    def on_start(self):
        print('Parent2 do something')

class Child(Parent1, Parent2):
    def on_start(self):
        for c in Child.__mro__[1:]:
            if 'on_start' in c.__dict__.keys():
                c.on_start(self)

>>> Child().on_start()
GGP1 do something
Parent2 do something
Khan -- father to all
但这也有一个问题——如果
Child
被进一步子类化,那么Child的子类也将在相同的
\uuuuuumro\uuuu
链上循环

正如Raymond Hettinger所述:

super()负责将方法调用委托给 实例的祖先树。对于要工作的可重定向方法调用, 这些类需要协同设计。这提出了三个问题 容易解决的实际问题:

1) super()调用的方法必须存在

2) 调用者和被调用者需要有匹配的参数签名和

3) 该方法的每次出现都需要使用super()


解决方案是编写协作类,通过祖先列表统一使用
super
,或者创造性地使用来调整您无法控制的类。雷蒙德·赫廷格(Raymond Hettinger)的文章更全面地讨论了这些方法

请尝试阅读可能的重复内容,我可以问一下为什么要进行向下投票,以及我如何改进这个答案吗?向下投票是因为它对
super
将调用的方法给出了误导性的解释,并且没有解释如何正确地执行协作多重继承。特别是,如果您试图从一个在
\uuuumro\uuuuu
上循环的类中进行多个继承,那么您正确调用所有祖先方法的选项将非常糟糕;特别是,再次循环
\uuu mro\uuuu
将导致某些方法被调用两次。@user2357112:哪个部分对
super
解析方法调用的顺序有误导性?super的解析顺序基于python的mro概念。我提供了多个链接来进一步解释这一点。如果其他祖先类本身在mro上循环,那么在这里循环mro怎么不好呢?哪个方法会被调用两次?这似乎有点苛刻……它给人的印象是,类中的
super
调用
Foo
将查看
Foo
的MRO,并始终调用
Foo
的祖先,而不是查看
类型(self)
的MRO,并可能调用在
Foo
的祖先中不存在的方法。另外,如果我们有
类B(C):
类A(B):
,并且
A.on\u start
B.on\u start
循环通过它们的
\u mro\uuuuu
,那么
C.on\u start
将被
A.on\u start
B.on\u start
调用。我同意你所说的。
class Ghengis(object):
    def on_start(self):
        print('Khan -- father to all')    

class GGP1(Ghengis):
    def on_start(self):
        print('GGP1 do something')

class GP1(GGP1):
    pass

class Parent1(GP1):
    pass        

class GGP2(Ghengis):
    pass

class GP2(GGP2):
    pass

class Parent2(GP2):
    def on_start(self):
        print('Parent2 do something')

class Child(Parent1, Parent2):
    def on_start(self):
        for c in Child.__mro__[1:]:
            if 'on_start' in c.__dict__.keys():
                c.on_start(self)

>>> Child().on_start()
GGP1 do something
Parent2 do something
Khan -- father to all