我有以下两个父类(super class):
class Parent1(object):
def on_start(self):
print('do something')
class Parent2(object):
def on_start(self):
print('do something else')
我希望有一个继承自两者的子类能够为 parent 双方调用 super。
class Child(Parent1, Parent2):
def on_start(self):
# super call on both parents
执行此操作的 Pythonic 方法是什么?谢谢。
最佳答案
执行摘要:
Super仅根据类层次结构的 __mro__
执行一个方法.如果你想执行一个以上的同名方法,你的父类需要编写以合作执行此操作(通过隐式或显式调用 super
)或者你需要循环 __bases__
或 __mro__
子类的值。
super
的工作是将部分或全部方法调用委托(delegate)给类祖先树中的某个现有方法。 委派可能会在您控制的类之外进行得很好。委托(delegate)的方法名需要存在于基类组中。
下面介绍的使用 __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,并在不知道按名称分类:
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
满足 once 类的每个父类:
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 的 child 也会循环遍历相同的 __mro__
链。
正如 Raymond Hettinger 所说:
super() is in the business of delegating method calls to some class in the instance’s ancestor tree. For reorderable method calls to work, the classes need to be designed cooperatively. This presents three easily solved practical issues:
1) the method being called by super() needs to exist
2) the caller and callee need to have a matching argument signature and
3) every occurrence of the method needs to use super()
解决方案是编写协作类,通过祖先列表或创造性地使用adapter pattern统一使用super
适应你无法控制的类(class)。这些方法在文章 Python’s super() considered super! 中进行了更完整的讨论。由雷蒙德·赫廷格 (Raymond Hettinger) 着。
关于Python 多重继承 : call super on all,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30353498/