Дополнительно
Понимание методов Python super() и __init__()
Я'пытаюсь понять использование super()
. Судя по всему, оба дочерних класса могут быть созданы, просто отлично.
Мне интересно узнать о фактической разнице между следующими двумя дочерними классами.
class Base(object):
def __init__(self):
print "Base created"
class ChildA(Base):
def __init__(self):
Base.__init__(self)
class ChildB(Base):
def __init__(self):
super(ChildB, self).__init__()
ChildA()
ChildB()
2357
7
super()
позволяет вам избежать явного обращения к базовому классу, что может быть полезно. Но главное преимущество появляется при множественном наследовании, где могут происходить всевозможные забавные вещи. Посмотрите стандартную документацию по super, если вы еще этого не сделали.Обратите внимание, что синтаксис изменился в Python 3.0: вы можете просто сказать
super().__init__()
вместоsuper(ChildB, self).__init__()
, что, IMO, гораздо приятнее. Стандартная документация также ссылается на guide to using super(), который достаточно толковый.Причина, по которой мы используем
super
, заключается в том, что дочерние классы, которые могут использовать кооперативное множественное наследование, будут вызывать правильную функцию следующего родительского класса в порядке разрешения методов (MRO).В Python 3 мы можем вызвать ее следующим образом:
В Python 2 мы должны использовать его следующим образом:
Без super вы ограничены в возможности использовать множественное наследование:
Я объясняю это ниже.
Основное различие в этом коде заключается в том, что вы получаете уровень косвенности в
__init__
сsuper
, который использует текущий класс для определения__init__
следующего класса для поиска в MRO.Я иллюстрирую эту разницу в ответе на канонический вопрос "Как использовать 'super' в Python?", который демонстрирует инъекцию зависимости и кооперативное множественное наследование.
Если бы в Python не было
super
.Вот код, который фактически эквивалентен
super
(как он реализован в C, за вычетом некоторых проверок и поведения отката, и переведен на Python):Написан немного более похоже на родной Python:
Если бы у нас не было объекта
super
, нам пришлось бы везде писать этот ручной код (или создавать его заново!), чтобы убедиться, что мы вызываем правильный следующий метод в порядке разрешения методов!Как super делает это в Python 3 без явного указания класса и экземпляра метода, из которого он был вызван?
Он получает кадр стека вызывающей функции и находит класс (неявно хранящийся как локальная свободная переменная
__class__
, что делает вызывающую функцию закрытием над классом) и первый аргумент этой функции, который должен быть экземпляром или классом, сообщающим ему, какой порядок разрешения методов (MRO) использовать.Поскольку для MRO требуется этот первый аргумент, использование
super
со статическими методами невозможно.Критика других ответов:
Это довольно волнообразно и не говорит нам многого, но смысл
super
не в том, чтобы избежать написания родительского класса. Смысл в том, чтобы обеспечить вызов следующего по очереди метода в порядке разрешения методов (MRO). This becomes important in multiple inheritance.I'll explain here.
And let's create a dependency that we want to be called after the Child:
Now remember,
ChildB
uses super,ChildA
does not:And
UserA
does not call the UserDependency method:But
UserB
, becauseChildB
usessuper
, does!:Criticism for another answer
In no circumstance should you do the following, which another answer suggests, as you'll definitely get errors when you subclass ChildB:
(That answer is not clever or particularly interesting, but in spite of direct criticism in the comments and over 17 downvotes, the answerer persisted in suggesting it until a kind editor fixed his problem.)
Explanation: That answer suggested calling super like this:
This is completely wrong.
super
позволяет нам искать следующего родителя в MRO (см. первый раздел этого ответа) для дочерних классов. Если вы скажетеsuper
, что мы находимся в методе дочернего экземпляра, он будет искать следующий по порядку метод (вероятно, этот), что приведет к рекурсии, возможно, вызовет логический сбой (в примере автора ответа так и есть) илиRuntimeError
, когда глубина рекурсии будет превышена.Было замечено, что в Python 3.0+ вы можете использовать
super().__init__()
для вызова, что является лаконичным и не требует явного обращения к именам родительских классов OR, что может быть удобно. Хочу добавить, что для Python 2.7 и младше можно добиться нечувствительного к имени поведения, написав
self.__class__
вместо имени класса, т.е.НО, это нарушает вызов
super
для любых классов, которые наследуются от вашего класса, гдеself.__class__
может вернуть дочерний класс. Например:Здесь у меня есть класс
Square
, который является подклассомRectangle
. Допустим, я не хочу писать отдельный конструктор дляSquare
, потому что конструктор дляRectangle
достаточно хорош, но по какой-то причине я хочу реализовать Square, чтобы я мог повторно реализовать какой-то другой метод.Когда я создаю
Square
с помощьюmSquare = Square('a', 10,10)
, Python вызывает конструктор дляRectangle
, потому что я не далSquare
собственного конструктора. Однако в конструкторе дляRectangle
вызовsuper(self.__class__,self)
вернет суперклассmSquare
, поэтому он снова вызывает конструктор дляRectangle
. Так происходит бесконечный цикл, о котором говорил @S_C. В этом случае, когда я выполняюsuper(...).__init__()
, я вызываю конструктор дляRectangle
, но поскольку я не даю ему аргументов, я получу ошибку.Супер не имеет побочных эффектов
работает, как ожидалось
попадает в бесконечную рекурсию.
Просто предупреждение... в Python 2.7, и я думаю, с тех пор, как
super()
был представлен в версии 2.2, вы можете вызватьsuper()
, только если один из родителей наследует от класса, который в конечном итоге наследуетobject
(new-style classes).Лично я, что касается кода python 2.7, собираюсь продолжать использовать
BaseClassName.__init__(self, args)
, пока не получу реальное преимущество от использованияsuper()
.На самом деле это не так. Для вызова методов
super()
обращается к следующему классу в MRO (порядок разрешения методов, доступ к которому осуществляется с помощьюcls.__mro__
). Просто вызов базового__init__
вызывает базовый__init__
. Так получилось, что в MRO есть только один элемент - база. Таким образом, вы делаете то же самое, но более приятным способом с помощьюsuper()
(особенно если вы позже займетесь множественным наследованием).Основное различие заключается в том, что
Ребенкаа.__метод init__
безоговорочно называют базой.ChildB инита
.метод initпозвонит
init, которая в Независимо от класса, случается,ChildB
прародителясамообороны
's линии предков (которые могут отличаться от того, что вы ожидаете).Если вы добавляете
ClassC
, который использует множественное наследование:затем "Базовый" - это уже не родитель
ChildB
для экземпляров ChildC. Теперь супер(ChildB, самовыдвижение)
указывает напримесь
еслия
- этоChildC
экземпляр.Вы вставили
примесь
междуChildB
ибаза
. И вы можете воспользоваться ею с супер()`Так что если вы создали свой занятия так, что они могут быть использованы в кооперативе несколько сценариев наследования, вы используете "супер" потому что вы Дон'т действительно знаю, кто собирается быть предком во время выполнения.
В супер считается супер пост и [команда PyCon 2015 видеоклипом][2] это очень хорошо объясняют.