执行摘要:
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 定义中定义的每个类层次结构。也就是说,它将遍历每个 forbearer 类,直到 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
现在想象一个更复杂的继承结构:
如果您想要每个 forbearer 的 on_start 方法,您可以使用 __mro__ 并过滤掉没有 on_start 作为该类的 __dict__ 一部分的类。否则,您可能会得到一个 forbearer 的 on_start 方法。换句话说,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 的孩子也会循环相同的 __mro__ 链。
正如 Raymond Hettinger 所说:
super() 的任务是将方法调用委托给某个类
实例的祖先树。为了使可重新排序的方法调用起作用,
这些课程需要合作设计。本次介绍三
轻松解决实际问题:
1) super() 调用的方法需要存在
2)调用者和被调用者需要有一个匹配的参数签名和
3)该方法的每次出现都需要使用super()
解决方案是编写协作类,通过祖先列表统一使用super,或者创造性地使用adapter pattern来适应您无法控制的类。这些方法在 Raymond Hettinger 的文章 Python’s super() considered super! 中进行了更完整的讨论。