Я вижу везде примеры, что методы суперкласса должны быть вызваны:
super(SuperClass, instance).method(args)
Есть ли недостаток:
SuperClass.method(instance, args)
Я вижу везде примеры, что методы суперкласса должны быть вызваны:
super(SuperClass, instance).method(args)
Есть ли недостаток:
SuperClass.method(instance, args)
Рассмотрим следующую ситуацию:
class A(object):
def __init__(self):
print('Running A.__init__')
super(A,self).__init__()
class B(A):
def __init__(self):
print('Running B.__init__')
# super(B,self).__init__()
A.__init__(self)
class C(A):
def __init__(self):
print('Running C.__init__')
super(C,self).__init__()
class D(B,C):
def __init__(self):
print('Running D.__init__')
super(D,self).__init__()
foo=D()
Итак, классы образуют так называемый алмаз наследования:
A
/ \
B C
\ /
D
Запуск кода дает
Running D.__init__
Running B.__init__
Running A.__init__
Это плохо, потому что C
__init__
пропущен. Причина этого в том, что B
__init__
вызывает A
__init__
напрямую.
Цель super
- разрешить алмазы наследования. Если вы не комментируете
# super(B,self).__init__()
и комментарий
A.__init__(self)
код дает более желательный результат:
Running D.__init__
Running B.__init__
Running C.__init__
Running A.__init__
Теперь все методы __init__
вызываются. Обратите внимание, что в то время, когда вы определяете B.__init__
, вы можете подумать, что super(B,self).__init__()
совпадает с вызовом A.__init__(self)
, но вы ошибаетесь. В приведенной выше ситуации super(B,self).__init__()
на самом деле вызывает C.__init__(self)
.
Святые курит, B
ничего не знает о C
, и тем не менее super(B,self)
знает, чтобы позвонить C
__init__
? Причина в том, что self.__class__.mro()
содержит C
. Другими словами, self
(или выше, foo
) знает о C
.
Поэтому будьте осторожны - они не являются взаимозаменяемыми. Они могут дать совершенно разные результаты.
Использование super
имеет подводные камни. Он требует значительного уровня координации между всеми классами диаграммы наследования. (Они должны, например, иметь одну и ту же сигнатуру вызова для __init__
, так как любой конкретный __init__
не знал, какой другой __init__
super
может вызвать следующий, или
else использовать **kwargs
.) Кроме того, вы должны быть совместимы с использованием super
везде. Пропустите его один раз (как в приведенном выше примере), и вы победите цель цели super
.
См. Ссылку для других подводных камней.
Если у вас есть полный контроль над иерархией классов или вы избегаете алмазов наследования, тогда нет необходимости в super
.
Нет никакого штрафа как есть, хотя ваш пример несколько ошибочен. В первом примере это должно быть
super(SubClass, instance).method(args) # Sub, not SuperClass
и это приводит меня к цитированию Документов Python:
Существует два типичных варианта использования для
super
. В иерархии классов с одиночным наследованиемsuper
может использоваться для обозначения родительских классов без их явного указания, что делает код более удобным для обслуживания. Это использование тесно совпадает с использованиемsuper
в других языках программирования.Второй вариант использования - поддерживать совместное множественное наследование в динамической среде исполнения. Этот вариант использования уникален для Python и не найден в статически скомпилированных языках или языках, которые поддерживают только одно наследование. Это позволяет реализовать "алмазные диаграммы", где несколько базовых классов реализуют один и тот же метод. Хороший дизайн диктует, что этот метод имеет одну и ту же сигнатуру вызова в каждом случае (поскольку порядок вызовов определяется во время выполнения, потому что этот порядок адаптируется к изменениям в иерархии классов и потому, что этот порядок может включать в себя классы-братья, неизвестные до времени выполнения).
В основном, используя первый метод, вам не нужно жестко программировать свой родительский класс для иерархических классов одного класса, и вы просто не можете делать то, что хотите (эффективно/эффективно), используя второй метод при использовании множественного наследования.