我有以下两个超类:
class Parent1(object):
def on_start(self):
print('do something')
class Parent2(object):
def on_start(self):
print('do something else')
Run Code Online (Sandbox Code Playgroud)
我希望有一个继承的子类可以为父母双方打电话.
class Child(Parent1, Parent2):
def on_start(self):
# super call on both parents
Run Code Online (Sandbox Code Playgroud)
什么是Pythonic的方法呢?谢谢.
daw*_*awg 17
执行摘要:
Super只根据类层次结构执行一种方法__mro__.如果要使用相同的名称执行多个方法,则需要编写父类来协同执行(通过super隐式或显式调用),或者需要循环遍历子类__bases__的__mro__值.
我们的工作super是将一部分或全部方法调用委托给祖先树中的一些现有方法.代表团可能会在您控制的课程之外进行.委派的方法名称需要存在于基类组中.
下面使用__bases__with的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()
Run Code Online (Sandbox Code Playgroud)
在这种情况下,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()
Run Code Online (Sandbox Code Playgroud)
如果要按方法名称调用多个父方法,则可以__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
Run Code Online (Sandbox Code Playgroud)
如果有可能有一个基类没有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"
Run Code Online (Sandbox Code Playgroud)
使用__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
Run Code Online (Sandbox Code Playgroud)
现在想象一个更复杂的继承结构:

如果你想要每一个forbearer的on_start方法,你可以使用__mro__并过滤掉那些没有on_start作为__dict__该类的一部分的类.否则,你可能会得到一个forbearer的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
Run Code Online (Sandbox Code Playgroud)
但这也有一个问题 - 如果Child进一步子类化,那么Child的孩子也会循环到同一个__mro__链上.
如Raymond Hettinger所述:
super()是将方法调用委托给实例的祖先树中的某个类的业务.对于可重新排序的方法调用,需要合作设计类.这提出了三个容易解决的实际问题:
1)super()调用的方法需要存在
2)调用者和被调用者需要具有匹配的参数签名和
3)每次出现的方法都需要使用super()
解决方案是编写合并类,super通过祖先列表或创建使用适配器模式统一使用,以适应您无法控制的类.这些方法在Python的super()文章中被更全面地讨论了!作者:Raymond Hettinger.