Любой подход («новый стиль» или «старый стиль») будет работать , если у вас есть контроль над исходным кодом для A
и B
.В противном случае может потребоваться использование класса адаптера.
Доступный исходный код: правильное использование «нового стиля»
class A(object):
def __init__(self):
print("-> A")
super(A, self).__init__()
print("<- A")
class B(object):
def __init__(self):
print("-> B")
super(B, self).__init__()
print("<- B")
class C(A, B):
def __init__(self):
print("-> C")
# Use super here, instead of explicit calls to __init__
super(C, self).__init__()
print("<- C")
>>> C()
-> C
-> A
-> B
<- B
<- A
<- C
Здесь порядок разрешения методов (MRO) диктуетследующее:
C(A, B)
сначала диктует A
, затем B
.MRO равно C -> A -> B -> object
. super(A, self).__init__()
продолжается по цепочке MRO, инициированной в C.__init__
до B.__init__
. super(B, self).__init__()
продолжается по цепочке MRO, инициированной в C.__init__
доobject.__init__
.
Можно сказать, что этот случай предназначен для множественного наследования .
Доступен исходный код: правильное использование "старого стиля"
class A(object):
def __init__(self):
print("-> A")
print("<- A")
class B(object):
def __init__(self):
print("-> B")
# Don't use super here.
print("<- B")
class C(A, B):
def __init__(self):
print("-> C")
A.__init__(self)
B.__init__(self)
print("<- C")
>>> C()
-> C
-> A
<- A
-> B
<- B
<- C
Здесь MRO не имеет значения, поскольку A.__init__
и B.__init__
вызываются явно.class C(B, A):
будет работать так же хорошо.
Хотя этот случай не "предназначен" для множественного наследования в новом стиле, как предыдущий, множественное наследование все еще возможно.
А теперь, что если A
и B
из сторонней библиотеки - то есть, у вас нет контроля над исходным кодом для A
и B
?Краткий ответ: Вы должны разработать класс адаптера, который реализует необходимые вызовы super
, а затем использовать пустой класс для определения MRO (см. статью Рэймонда Хеттингера по super
- особенно раздел "Как«Включить некооперативный класс»).
Сторонние родители: A
не реализует super
;B
делает
class A(object):
def __init__(self):
print("-> A")
print("<- A")
class B(object):
def __init__(self):
print("-> B")
super(B, self).__init__()
print("<- B")
class Adapter(object):
def __init__(self):
print("-> C")
A.__init__(self)
super(Adapter, self).__init__()
print("<- C")
class C(Adapter, B):
pass
>>> C()
-> C
-> A
<- A
-> B
<- B
<- C
Класс Adapter
реализует super
, так что C
может определять MRO, который вступает в игру при выполнении super(Adapter, self).__init__()
.
А что, если все наоборот?
Сторонние родители: A
реализует super
;B
не
class A(object):
def __init__(self):
print("-> A")
super(A, self).__init__()
print("<- A")
class B(object):
def __init__(self):
print("-> B")
print("<- B")
class Adapter(object):
def __init__(self):
print("-> C")
super(Adapter, self).__init__()
B.__init__(self)
print("<- C")
class C(Adapter, A):
pass
>>> C()
-> C
-> A
<- A
-> B
<- B
<- C
Та же схема здесь, за исключением того, что порядок выполнения переключен в Adapter.__init__
;super
сначала вызови, затем явный вызов.Обратите внимание, что в каждом случае со сторонними родителями требуется уникальный класс адаптера.
Поэтому кажется, что, если я не знаю / не контролирую init классов, которые я наследую (A
и B
)Я не могу сделать безопасный выбор для класса, который пишу (C
).
Хотя вы можете справиться со случаями, когда вы не контролируете исходный кодA
и B
с использованием класса адаптера, это правда, что вы должны знать , как init для родительских классов реализуют super
(если вообще), чтобы сделать это.