[英]Python Multiple Inheritance: call super on all
我有以下兩個超類:
class Parent1(object):
def on_start(self):
print('do something')
class Parent2(object):
def on_start(self):
print('do something else')
我想要一個孩子 class 繼承自兩者都能夠為父母雙方調用超級。
class Child(Parent1, Parent2):
def on_start(self):
# super call on both parents
執行此操作的 Pythonic 方法是什么? 謝謝。
執行摘要:
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
只有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
類,直到on_start
類的每個父級滿足一次 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
的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__
鏈上循環。
正如Raymond Hettinger所說:
super()用於將方法調用委派給實例的祖先樹中的某個類。 為了使可重排序的方法調用起作用,需要協同設計類。 這提出了三個容易解決的實際問題:
1)super()調用的方法必須存在
2)主叫方和被叫方需要具有匹配的參數簽名,並且
3)方法的每次出現都需要使用super()
解決方案是編寫在祖先列表中統一使用super
合作類,或者創造性地使用適配器模式來適應您無法控制的類。 在Python的super()文章super!中更全面地討論了這些方法。 由Raymond Hettinger撰寫。
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)
在您的情況下,由於兩個父對象都實現了相同的方法,因此super
將從左到右與繼承的第一個父對象相同(對於您的代碼, Parent1
)。 用super
調用兩個函數是不可能的。 要執行所需的操作,必須簡單地從父類中調用方法,如下所示:
class Child(Parent1, Parent2):
def on_start (self):
Parent1.on_start()
Parent2.on_start()
super()
調用是為了合作。
一個標准的成語如下:
object.__init__
時發生異常)。super()
,包括基數。__init__
接受**kwargs
; 根據特定的關鍵字初始化本地的東西; 然后沿着super
鏈傳遞信息。所以,例如:
class Person:
def __init__(self, **kwargs):
print(f'Initializing a Person')
pass
class Parent1(Person):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.heirloom1 = kwargs.get('heirloom1', None)
print(f'Set up Parent1 of the {self.__class__} with {self.heirloom1!r}')
class Parent2(Person):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.heirloom2 = kwargs.get('heirloom2', None)
print(f'Set up Parent2 of the {self.__class__} with {self.heirloom2!r}')
class Child(Parent1, Parent2):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.inheritance = kwargs.get('inheritance', None)
spoiled_rotten = Child(inheritance='mansion', heirloom1='trust fund', heirloom2='sports car')
特別注意,當spoiled_rotten
被創建時, super().__init__(**kwargs)
將轉發給Parent2
,而不是Person
,並且Person.__init__
只被調用一次。 super
並不意味着“基類”; 它的意思是“ self
的 MRO 中的下一個 class”——而self
在整個過程中都是一個Child
實例。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.