执行摘要:
Super 仅根据类层次结构的__mro__ 执行一种方法。如果您想以相同的名称执行多个方法,则需要编写父类以协作执行此操作(通过隐式或显式调用 super),或者您需要循环 __bases__ 或 __mro__ 的值子类。
super 的工作是将部分或全部方法调用委托给类祖先树中的某个现有方法。 委托可能会在你控制的类之外很好地进行。委托的方法名需要存在于基类组中。
下面介绍的使用__bases__ 和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_start 方法。调用super 解决了只有Parent3 有on_start,这就是被调用的方法。
如果Child 继承自多个具有on_start 方法的类,则顺序从左到右(如类定义中所列)和从下到上(作为逻辑继承)解析。 只有一个方法被调用,类层次结构中的其他同名方法已被取代。
所以,更常见的是:
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()
如果你想通过方法名调用多个父方法,你可以在这种情况下使用__bases__而不是super,并在不知道类名的情况下遍历Child的基类:
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
如果有可能基类之一没有on_start,您可以使用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"
使用__bases__ 的行为类似于super,但对于Child 定义中定义的每个类层次结构。即,它将遍历每个前辈类,直到 on_start 满足该类的每个父级一次:
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
现在想象一个更复杂的继承结构:
如果您想要每个前辈的on_start 方法,您可以使用__mro__ 并过滤掉没有on_start 的类作为该类的__dict__ 的一部分。否则,你可能会得到一个前辈的on_start 方法。换句话说,hassattr(c, 'on_start') 是True 对于Child 是其后代的每个类(在这种情况下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 的子级也会循环到相同的__mro__ 链上。
正如雷蒙德·赫廷格所说:
super() 负责将方法调用委托给某个类
实例的祖先树。为了使可重新排序的方法调用正常工作,
这些类需要合作设计。这提出了三个
容易解决的实际问题:
1) super() 调用的方法需要存在
2) 调用者和被调用者需要有一个匹配的参数签名和
3) 每次出现的方法都需要使用super()
解决方案是编写协作类,通过祖先列表统一使用super 或创造性地使用adapter pattern 来适配你无法控制的类。这些方法在 Raymond Hettinger 的文章Python’s super() considered super! 中有更完整的讨论。