super() позволяет избежать явных ссылок на базовый класс, что может быть неплохо. Но главное преимущество заключается в множественном наследовании, где могут происходить всевозможные забавные вещи. Ознакомьтесь со стандартными документами по super, если вы еще этого не сделали.
Обратите внимание, что синтаксис изменился в Python 3.0: вы можете просто сказать, super().__init__() вместо super(ChildB, self).__init__() какой IMO немного приятнее. В стандартных документах также содержится ссылка на руководство по использованию, super() которое является достаточно пояснительным.
Ответ 2
Я пытаюсь понять super()
Причина, по которой мы используем super, заключается в том, что дочерние классы, которые могут использовать совместное множественное наследование, будут вызывать правильную функцию следующего родительского класса в порядке разрешения метода (MRO).
В Python 3 мы можем назвать это следующим образом:
В Python 2 мы должны были вызывать super подобным образом с определяющим именем класса и self, но с этого момента мы будем избегать этого, потому что это избыточно, медленнее (из-за поиска имен) и более подробно (поэтому обновите свой Python, если вы еще этого не сделали!):
super(ChildB, self).__init__()
Без super вы ограничены в своих возможностях использовать множественное наследование, потому что вы жестко настраиваете следующий родительский вызов:
Основное отличие в этом коде заключается в том, что в ChildB вы получаете уровень косвенности в __init__ with super , который использует класс, в котором он определен, для определения следующего класса, который __init__ нужно искать в MRO.
Вот код, который на самом деле близко эквивалентен super (как он реализован на C, за вычетом некоторой проверки и резервного поведения, и переведен на Python):
classChildB(Base): def__init__(self): mro = type(self).mro() check_next = mro.index(ChildB) + 1# next after *this* class. while check_next < len(mro): next_class = mro[check_next] if'__init__'in next_class.__dict__: next_class.__init__(self) break check_next += 1
Написано немного больше как родной Python:
classChildB(Base): def__init__(self): mro = type(self).mro() for next_class in mro[mro.index(ChildB) + 1:]: # slice to end ifhasattr(next_class, '__init__'): next_class.__init__(self) break
Если бы у нас не было super объекта, нам пришлось бы писать этот ручной код везде (или воссоздавать его заново!), Чтобы гарантировать, что мы вызываем правильный следующий метод в порядке разрешения метода!
Как super делает это в Python 3, не указывая явно, из какого класса и экземпляра метода он был вызван?
Он получает вызывающий фрейм стека и находит класс (неявно сохраненный как локальная свободная переменная, __class__, что делает вызывающую функцию замыканием на класс) и первый аргумент этой функции, который должен быть экземпляром или классом, который сообщает ей, какой порядок разрешения метода (MRO) использовать.
функция super() позволяет избежать явных ссылок на базовый класс, что может быть неплохо. . Но главное преимущество заключается в множественном наследовании, где могут происходить всевозможные забавные вещи. Ознакомьтесь со стандартными документами по super, если вы еще этого не сделали.
Это довольно размашисто и мало что нам говорит, но смысл super не в том, чтобы избежать написания родительского класса. Суть в том, чтобы убедиться, что вызывается следующий в очереди метод в порядке разрешения методов (MRO). Это становится важным при множественном наследовании.
>>> UserA() UserA init'ed ChildA init'ed Base init'ed <__main__.UserA object at 0x0000000003403BA8>
Но UserB на самом деле вызывает UserDependency, потому что ChildB вызывает super:
>>> UserB() UserB init'ed ChildB init'ed UserDependency init'ed Base init'ed <__main__.UserB object at 0x0000000003403438>
Критика за другой ответ
Ни при каких обстоятельствах вы не должны делать следующее, что предлагается в другом ответе, поскольку вы определенно будете получать ошибки при создании подкласса ChildB:
super(self.__class__, self).__init__() # DON'T DO THIS! EVER.
(Этот ответ не является умным или особенно интересным, но, несмотря на прямую критику в комментариях и более 17 отрицательных отзывов, автор ответа продолжал предлагать его, пока добрый редактор не исправил его проблему.)
Объяснение: Использование self.__class__ в качестве замены имени класса в super() приведет к рекурсии. super позволяет нам найти следующий родительский элемент в MRO (см. Первый раздел Этого ответа) для дочерних классов. Если вы скажете, что super мы находимся в методе дочернего экземпляра, он затем выполнит поиск следующего метода в строке (возможно, этого), что приведет к рекурсии, вероятно, вызывающей логический сбой (в примере ответчика так и есть) или RuntimeError при превышении глубины рекурсии.
Новый метод вызова super() в Python 3, к счастью, позволяет нам обойти эту проблему.
Ответ 3
Было отмечено, что в Python 3.0+ вы можете использовать
super().__init__()
для выполнения вашего вызова, который является кратким и не требует от вас явной ссылки на родительские имена ИЛИ имена классов, что может быть удобно. Я просто хочу добавить, что для Python 2.7 или ниже некоторые люди реализуют поведение, не зависящее от имени, написав self.__class__ вместо имени класса, т.е.
super(self.__class__, self).__init__() # DON'T DO THIS!
ОДНАКО это прерывает вызовы super для любых классов, наследуемых от вашего класса, где self.__class__ может возвращаться дочерний класс. Например:
classPolygon(object): def__init__(self, id): self.id = id
Здесь у меня есть класс 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 но поскольку я не даю ему аргументов, я получу сообщение об ошибке.