Я пытаюсь понять, как super работает в множественном наследовании python для суперклассов, например, в приведенном ниже коде:
class First():
def __init__(self, parm1, **kwargs):
super().__init__(**kwargs)
self.parm1 = parm1
self.parm3 = 'one'
class Second():
def __init__(self, parm2 = 'zero', **kwargs):
super().__init__(**kwargs)
self.parm2 = parm2
class Third(First,Second):
def __init__(self,parm1):
super().__init__(parm1=parm1)
trd = Third('tst')
print(trd.parm1) # 'tst'
print(trd.parm3) # 'one'
print(trd.parm2) # 'zero'
Если я удалю super().__init__(**kwargs)
, выполнение закончится
«Третий» объект не имеет атрибута «parm2»
печатая только parm1
и parm3
, даже если я объявил иерархию в Class Third(First,Second)
.
Я знаю, что все классы наследуются от класса Object, но я не понимаю, как он может быть связан с классом super() в родительских классах и как последний позволяет получить доступ к атрибутам второго родителя.
Смотрите https://realpython.com/lessons/multiple-inheritance-python/
Базовые классы применяются слева направо, т.е. самый базовый класс находится справа.
например в class Third(First,Second)
вещи из First
переопределяют вещи из Second
. то есть Second
здесь самый базовый класс. (Судя по названию, вы наверное ожидали обратного?)
Мы также можем увидеть это, проверив «Порядок разрешения метода» Third
:
In [1]: Third.__mro__
Out[1]: (__main__.Third, __main__.First, __main__.Second, object)
Мы можем прочитать этот кортеж как потомок->родитель в порядке слева направо. См. https://stackoverflow.com/a/2010732/202168
Еще одна вещь, которую нужно знать, это то, что super().__init__()
найдет другой родительский класс для вызова в зависимости от контекста.
Например, First
не наследуется от Second
, поэтому, если вы создадите экземпляр First
, вызов super
перейдет прямо к object
. Мы можем увидеть это в MRO:
In [2]: First.__mro__
Out[2]: (__main__.First, object)
Но когда мы создаем экземпляр Third
, этот класс наследуется как от First
, так и от Second
... поэтому цепочка вызовов super
будет отражать порядок в Third.__mro__
... это означает, что вызов super
в First
теперь будет вызывать метод в Second
(поскольку Second
следующий родительский класс с точки зрения дочернего класса, инициировавшего super
цепочку вызовов). И вызов super
в Second
вызовет __init__
из object
, где заканчивается цепочка.
Все это означает, что если убрать super().__init__(**kwargs)
из First
, то цепочка супервызовов обрывается, не дойдя до Second.__init__
.
Следовательно, вы получаете AttributeError
«Третий объект не имеет атрибута« parm2 »», когда пытаетесь print(trd.parm2)
.
Хорошо, спасибо, так что если я понимаю: - Порядок переопределения справа налево от круглых скобок класса - super().__init__(**kwargs)
похож на «связь» через различные наследования, поэтому это необходимо только тогда, когда есть «горизонтальное» наследование
super()
возвращает «следующий» родительский класс (и это разрешается динамически в зависимости от того, какой экземпляр в конечном итоге является «верхним» или дочерним классом). Если вы хотите, чтобы ваши методы хорошо работали в иерархии классов, они обычно должны использовать super()
для вызова одного и того же метода в любом родительском классе, который может существовать. Таким образом, в случае метода __init__
это почти всегда требуется, потому что все классы будут иметь такой метод (включая корневой тип object
)
super()
то же, чтоsuper(__class__, <first argument>)