Python multiple inheritance передаёт аргументы конструкторам, использующим super
Рассмотрим следующий фрагмент кода Python
class A(object):
def __init__(self, a):
self.a = a
class B(A):
def __init__(self, a, b):
super(B, self).__init__(a)
self.b = b
class C(A):
def __init__(self, a, c):
super(C, self).__init__(a)
self.c = c
class D(B, C):
def __init__(self, a, b, c, d):
#super(D,self).__init__(a, b, c) ???
self.d = d
Мне интересно, как я могу передать a
, b
и c
конструкторам соответствующих базовых классов.
Ответы
Ответ 1
Ну, имея дело с множественным наследованием в целом, ваши базовые классы (к сожалению) должны быть разработаны для множественного наследования. Классы B
и C
в вашем примере отсутствуют, и поэтому вы не смогли найти правильный способ применения super
в D
.
Один из распространенных способов проектирования базовых классов для множественного наследования заключается в том, что базовые классы среднего уровня принимают дополнительные аргументы в свой метод __init__
, который они не намереваются использовать, и передавать их вместе с их super
вызов.
Здесь один способ сделать это в python:
class A(object):
def __init__(self,a):
self.a=a
class B(A):
def __init__(self,b,**kw):
self.b=b
super(B,self).__init__(**kw)
class C(A):
def __init__(self,c,**kw):
self.c=c
super(C,self).__init__(**kw)
class D(B,C):
def __init__(self,a,b,c,d):
super(D,self).__init__(a=a,b=b,c=c)
self.d=d
Это можно считать разочаровывающим, но это так, как есть.
Ответ 2
К сожалению, нет способа заставить это работать с использованием super()
без изменения базовых классов. Любой вызов конструкторов для B
или C
будет пытаться вызвать следующий класс в Порядке разрешения методов, который всегда будет B
или C
вместо класса A
который принимают конструкторы классов B
и C
Альтернативой является явный вызов конструкторов без использования super()
в каждом классе.
class A(object):
def __init__(self, a):
object.__init__()
self.a = a
class B(A):
def __init__(self, a, b):
A.__init__(self, a)
self.b = b
class C(object):
def __init__(self, a, c):
A.__init__(self, a)
self.c = c
class D(B, C):
def __init__(self, a, b, c, d):
B.__init__(self, a, b)
C.__init__(self, a, c)
self.d = d
Здесь все еще есть недостаток, так как конструктор A
будет вызываться дважды, что на самом деле не имеет большого эффекта в этом примере, но может вызвать проблемы в более сложных конструкторах. Вы можете включить проверку, чтобы конструктор не запускался более одного раза.
class A(object):
def __init__(self, a):
if hasattr(self, 'a'):
return
# Normal constructor.
Некоторые назвали бы это недостатком super()
, и это в некотором смысле, но это также просто недостаток множественного наследования в целом. Образцы алмазного наследования часто подвержены ошибкам. И многие обходные пути для них приводят к еще более запутанному и подверженному ошибкам коду. Иногда лучшим ответом является попытка рефакторинга вашего кода для использования менее множественного наследования.
Ответ 3
"Мне интересно, как я могу передать a, b и c конструкторам соответствующих базовых классов". Вы можете сделать это двумя способами: Первый (как в ответе выше): B. init (self, a, b) C. init (self, a, c) Секунды: super(). init (a, b) super(). init (a, c), но атрибут будет изменен до последнего вызова, вот super(). init (a, c). Я пытался использовать MRO с конструкторами, имеющими аргументы с одинаковыми именами некоторых атрибутов, но у меня нет ответов. Кто-то может это сделать????