我有以下两个超类:
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
Pythonic的做法是什么?感谢。
答案 0 :(得分:17)
执行摘要:
Super仅根据类层次结构的__mro__
执行一个方法。如果要使用相同的名称执行多个方法,则需要编写父类来协同执行(通过隐式或显式调用super
)或者需要循环__bases__
或{ {3}}子类的值。
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')
对于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__
链。
如Raymond Hettinger所述:
super()是将方法调用委托给某些类的业务 实例的祖先树。对于可重新排序的方法调用, 这些课程需要合作设计。这提出了三个 轻松解决实际问题:
1)super()调用的方法需要存在
2)调用者和被调用者需要具有匹配的参数签名和
3)每次出现的方法都需要使用super()
解决方案是编写合作类,通过祖先列表统一使用super
或创建使用__mro__
来适应您无法控制的类。 Raymond Hettinger在文章adapter pattern中更全面地讨论了这些方法。
答案 1 :(得分:1)
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):
super(Child, self).on_start()
super(Parent1, self).on_start()
c = Child()
c.on_start()
do something
do something else
或者没有超级:
class Child(Parent1, Parent2):
def on_start(self):
Parent1.on_start(self)
Parent2.on_start(self)
答案 2 :(得分:0)
在您的情况下,由于两个父项都实现了相同的方法,super
将与从左到右(对于您的代码Parent1
)继承的第一个父项相同。使用super
调用两个函数是不可能的。要做你想做的事,你必须简单地从父类调用方法,如下所示:
class Child(Parent1, Parent2):
def on_start (self):
Parent1.on_start()
Parent2.on_start()