Какая разница между:
class Child(SomeBaseClass):
def __init__(self):
super(Child, self).__init__()
а также:
class Child(SomeBaseClass):
def __init__(self):
SomeBaseClass.__init__(self)
Я видел, super
что довольно часто используется в классах с единственным наследованием. Я могу понять, почему вы используете его в множественном наследовании, но неясно, каковы преимущества его использования в такой ситуации.
источник
Какая разница?
средство для вызова
SomeBaseClass
«S__init__
. покаозначает вызвать границу
__init__
из родительского класса, который следуетChild
в Порядке разрешения метода (MRO).Если экземпляр является подклассом Child, то в MRO может быть другой родитель.
Объяснил просто
Когда вы пишете класс, вы хотите, чтобы другие классы могли использовать его.
super()
облегчает для других классов использование класса, который вы пишете.Как говорит Боб Мартин, хорошая архитектура позволяет вам откладывать принятие решений как можно дольше.
super()
может включить такую архитектуру.Когда другой класс наследует класс, который вы написали, он также может наследоваться от других классов. И эти классы могут иметь
__init__
после этого__init__
порядок упорядочения классов для разрешения методов.Без
super
вы, вероятно, жестко закодировали бы родительский класс класса, который вы пишете (как в примере). Это будет означать, что вы не будете вызывать next__init__
в MRO и, следовательно, не сможете повторно использовать код в нем.Если вы пишете свой собственный код для личного использования, вас может не волновать это различие. Но если вы хотите, чтобы другие использовали ваш код, использование
super
- это то, что обеспечивает большую гибкость для пользователей кода.Питон 2 против 3
Это работает в Python 2 и 3:
Это работает только в Python 3:
Он работает без аргументов, перемещаясь вверх по фрейму стека и получая первый аргумент метода (обычно
self
для метода экземпляра илиcls
для метода класса - но могут быть и другие имена) и находя класс (напримерChild
) в свободных переменных ( это ищется с именем__class__
как свободная переменная замыкания в методе).Я предпочитаю продемонстрировать кросс-совместимый способ использования
super
, но если вы используете только Python 3, вы можете вызывать его без аргументов.Непрямое движение с прямой совместимостью
Что это тебе дает? Для одиночного наследования примеры из вопроса практически идентичны с точки зрения статического анализа. Однако использование
super
дает вам уровень косвенности с прямой совместимостью.Прямая совместимость очень важна для опытных разработчиков. Вы хотите, чтобы ваш код продолжал работать с минимальными изменениями по мере его изменения. Когда вы просматриваете историю изменений, вы хотите увидеть, что именно изменилось, когда.
Вы можете начать с одного наследования, но если вы решите добавить другой базовый класс, вам нужно будет только изменить строку с базами - если базы изменятся в классе, от которого вы наследуете (скажем, добавлен миксин), вы измените ничего в этом классе. В частности, в Python 2 получение
super
правильных аргументов и правильных аргументов метода может быть затруднено. Если вы знаете, что вы используетеsuper
правильно с одиночным наследованием, это сделает отладку менее сложной в будущем.Внедрение зависимости
Другие люди могут использовать ваш код и ввести родителей в разрешение метода:
Допустим, вы добавили еще один класс к своему объекту и хотите добавить класс между Foo и Bar (для тестирования или по какой-либо другой причине):
При использовании дочернего элемента un-super не удается внедрить зависимость, поскольку используемый вами дочерний элемент жестко запрограммировал метод, вызываемый после его собственного:
Однако класс с дочерним элементом, который использует,
super
может правильно внедрить зависимость:Обращаясь к комментарию
Python линеаризует сложное дерево наследования через алгоритм линеаризации C3, чтобы создать Порядок разрешения методов (MRO).
Мы хотим, чтобы методы рассматривались в этом порядке .
Чтобы метод, определенный в родительском элементе, чтобы найти следующий в этом порядке без него
super
, он долженUnsuperChild
Вовсе не имеют доступа кInjectMe
. Это тот,UnsuperInjector
который имеет доступInjectMe
- и все же не может вызвать метод этого класса из метода, от которого он наследуетсяUnsuperChild
.Оба дочерних класса намереваются вызвать метод с тем же именем, которое будет следующим в MRO, что может быть другим классом, о котором он не знал, когда создавался.
Тот, у которого нет
super
жестких кодов метода его родителя - поэтому он ограничил поведение своего метода, и подклассы не могут вводить функциональность в цепочку вызовов.Один с
super
имеет большую гибкость. Цепочка вызовов для методов может быть перехвачена, а функциональность введена.Вам может не понадобиться эта функциональность, но могут быть подклассы вашего кода.
Вывод
Всегда используйте
super
для ссылки на родительский класс вместо жесткого его кодирования.Вы намереваетесь сослаться на следующий родительский класс, а не тот, от которого наследует дочерний класс.
Неиспользование
super
может наложить ненужные ограничения на пользователей вашего кода.источник
list
интерфейса, скажем,doublylinkedlist
тогда приложение плавно выберет ее. Я могу сделать мой пример более настраиваемым, представивconfig.txt
и связав реализацию во время загрузки. Это правильный пример? Если да, как мне соотнести ваш код? Смотрите первый совет DI в вики. Где настраивается любая новая реализация? в вашем кодеInjectMe
класса. Однако комментарии не предназначены для обсуждения, поэтому я предлагаю вам обсудить это в чате с другими или задать новый вопрос на главном сайте.__init__
функциями. особенно если сигнатура__init__
варьируется между классами в иерархии. Я добавил ответ, который фокусируется на этом аспектеЯ немного поиграл
super()
и понял, что мы можем изменить порядок звонков.Например, у нас есть следующая иерархическая структура:
В этом случае MRO D будет (только для Python 3):
Давайте создадим класс, где
super()
вызовы после выполнения метода.Итак, мы видим, что порядок разрешения такой же, как в MRO. Но когда мы вызываем
super()
в начале метода:У нас другой порядок, это обратный порядок кортежа MRO.
Для дополнительного чтения я бы порекомендовал следующие ответы:
источник
Разве все это не предполагает, что базовый класс является классом нового стиля?
Не будет работать в Python 2.
class A
должен быть в новом стиле, то есть:class A(object)
источник
При вызове
super()
разрешения для родительской версии метода класса, метода экземпляра или статического метода мы хотим передать текущий класс, область действия которого мы находимся в качестве первого аргумента, чтобы указать, в какую область родительского элемента мы пытаемся разрешить, и как второй аргумент интересующий объект, чтобы указать, к какому объекту мы пытаемся применить эту область.Рассмотрим иерархию классов
A
,B
иC
где каждый класс является родителем одного следующего за ним, иa
,b
иc
соответствующие экземпляры каждого.Использование
super
со статическим методомнапример, используя
super()
изнутри__new__()
методОбъяснение:
1 - хотя обычно
__new__()
в качестве первого параметра принято ссылаться на вызывающий класс, в Python он реализован не как метод класса, а как статический метод. То есть ссылка на класс должна быть явно передана в качестве первого аргумента при__new__()
непосредственном вызове :2 - при вызове
super()
get для родительского класса мы передаем дочерний класс вA
качестве первого аргумента, затем передаем ссылку на интересующий объект, в данном случае это ссылка на класс, которая была передана приA.__new__(cls)
вызове. В большинстве случаев это также является ссылкой на дочерний класс. В некоторых ситуациях это может быть не так, например, в случае наследования нескольких поколений.3 - поскольку, как правило,
__new__()
является статическим методом,super(A, cls).__new__
он также возвращает статический метод, и в этом случае необходимо явно указать все аргументы, включая ссылку на объект интересаcls
.4- делать то же самое без
super
Использование
super
с методом экземпляранапример, используя
super()
изнутри__init__()
Объяснение:
1
__init__
- это метод экземпляра, означающий, что в качестве первого аргумента он принимает ссылку на экземпляр. При вызове непосредственно из экземпляра ссылка передается неявно, то есть вам не нужно указывать ее:2- при вызове
super()
внутри__init__()
мы передаем дочерний класс в качестве первого аргумента, а интересующий объект - в качестве второго аргумента, который в общем случае является ссылкой на экземпляр дочернего класса.3. Вызов
super(A, self)
возвращает прокси-сервер, который разрешит область и применит ее,self
как если бы это был экземпляр родительского класса. Давайте назовем этот проксиs
. Так__init__()
как это метод экземпляра, вызовs.__init__(...)
неявно передаст ссылку вself
качестве первого аргумента родителю__init__()
.4 - чтобы сделать то же самое без
super
нас, нам нужно явно передать ссылку на экземпляр в родительскую версию__init__()
.Использование
super
с методом классаОбъяснение:
1. Метод класса может быть вызван напрямую из класса и в качестве первого параметра принимает ссылку на класс.
2 - при вызове
super()
метода класса для разрешения его родительской версии мы хотим передать текущий дочерний класс в качестве первого аргумента, чтобы указать, в какую область видимости родителя мы пытаемся разрешить, и интересующий объект в качестве второго аргумента чтобы указать, к какому объекту мы хотим применить эту область, которая в общем случае является ссылкой на сам дочерний класс или один из его подклассов.3- Вызов
super(B, cls)
разрешается в объемеA
и применяется к немуcls
. Такalternate_constructor()
как это метод класса, вызовsuper(B, cls).alternate_constructor(...)
неявно передаст ссылку вcls
качестве первого аргумента вA
версиюalternate_constructor()
4 - чтобы сделать то же самое без использования,
super()
вам необходимо получить ссылку на несвязанную версиюA.alternate_constructor()
(то есть явную версию функции). Просто сделать это не будет работать:Выше не будет работать, потому что
A.alternate_constructor()
метод принимает неявную ссылку вA
качестве первого аргумента.cls
Существо прошло здесь будет его второй аргумент.источник
Много отличных ответов, но для визуальных учеников: сначала давайте рассмотрим с аргументами супер, а затем без.
Представьте себе экземпляр,
jack
созданный из классаJack
, у которого есть цепочка наследования, как показано зеленым на рисунке. Вызов:super(Jack, jack).method(...)
будет использовать MRO (Порядок разрешения методов) of
jack
(его дерево наследования в определенном порядке) и начнет поиск сJack
. Почему можно предоставить родительский класс? Хорошо, если мы начнем поиск с экземпляраjack
, он найдет метод экземпляра, и все дело в том, чтобы найти метод его родителей.Если кто-то не предоставляет аргументы для super, то он, как и первый передаваемый аргумент, является классом
self
, а второй передаваемый аргумент -self
. Они автоматически рассчитываются для вас в Python3.Однако, скажем, мы не хотим использовать
Jack
метод, вместо того, чтобы передатьJack
, мы могли бы передать,Jen
чтобы начать поиск метода сверхуJen
.Он выполняет поиск по одному слою за раз (ширина, а не глубина), например, если
Adam
и уSue
обоих есть требуемый метод, один изSue
будет найден первым.Если
Cain
и то иSue
другое имеет требуемый метод,Cain
метод будет вызван первым. Это соответствует в коде:MRO слева направо.
источник
некоторые отличные ответы здесь, но они не решают, как использовать
super()
в случае, когда разные классы в иерархии имеют разные подписи ... особенно в случае__init__
чтобы ответить на эту часть и иметь возможность эффективно использовать,
super()
я бы предложил прочитать мой ответ super () и изменить сигнатуру кооперативных методов .Вот только решение этого сценария:
пример использования:
вывод:
источник
Это довольно легко понять.
Хорошо, что происходит сейчас, если вы используете
super(Child,self)
?Когда создается дочерний экземпляр, его MRO (Порядок разрешения метода) имеет порядок (Child, SomeBaseClass, object) в зависимости от наследования. (предположим, что SomeBaseClass не имеет других родителей, кроме объекта по умолчанию)
Передавая
Child, self
,super
выполняет поиск в MROself
экземпляра и возвращает прокси-объект, следующий за Child, в данном случае это SomeBaseClass, этот объект затем вызывает__init__
метод SomeBaseClass. Другими словами, если это такsuper(SomeBaseClass,self)
,super
возвращаемый прокси-объект будетobject
Для множественного наследования MRO может содержать много классов, поэтому, в основном,
super
вы можете решить, где вы хотите начать поиск в MRO.источник
Рассмотрим следующий код:
Если изменить конструктор
Y
наX.__init__
, вы получите:Но используя
super(Y, self).__init__()
, вы получите:И
P
илиQ
даже могут быть задействованы из другого файла, который вы не знаете, когда пишетеX
иY
. Так что, по сути, вы не будете знать, на чтоsuper(Child, self)
будет ссылаться, когда будете писатьclass Y(X)
, даже подпись Y так же проста, как иY(X)
. Вот почему супер может быть лучшим выбором.источник