2013-10-26 2 views
125

В Python 3.x, super() можно назвать без аргументов:Почему супер() магия Python 3.x?

class A(object): 
    def x(self): 
     print("Hey now") 

class B(A): 
    def x(self): 
     super().x() 
>>> B().x() 
Hey now 

Для того, чтобы сделать эту работу, некоторые во время компиляции магия выполняется, следствием чего является то, что следующий код (который выполняет повторную привязку к supersuper_) не:

super_ = super 

class A(object): 
    def x(self): 
     print("No flipping") 

class B(A): 
    def x(self): 
     super_().x() 
>>> B().x() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 3, in x 
RuntimeError: super(): __class__ cell not found 

Почему super() не в состоянии Рез ольве суперкласса во время выполнения без помощи компилятора? Существуют ли практические ситуации, в которых такое поведение или его основная причина могут укусить неосторожного программиста?

... и в качестве побочного вопроса: есть ли в Python другие примеры функций, методов и т. Д., Которые можно сломать, перевязывая их под другим именем?

+6

Я дам Armin сделать объяснения по этому [один] (http://lucumr.pocoo.org/2010/1/7/плюсы-и-против-о-питон-3 /). Это еще один хороший [post] (http://lucumr.pocoo.org/2013/5/21/porting-to-python-3-redux/) –

+0

, связанный: http://stackoverflow.com/q/36993577/674039 – wim

ответ

184

Новое поведение магии super() было добавлено, чтобы избежать нарушения D.R.Y. (Не повторяйте себя), см. PEP 3135. Имея явно назвать класс, ссылаясь как глобальный также склонно к тем же проблемам пересвязывания вы обнаружили с super() себе:

class Foo(Bar): 
    def baz(self): 
     return super(Foo, self).baz() + 42 

Spam = Foo 
Foo = something_else() 

Spam().baz() # liable to blow up 

То же самое относится к использованию класса декораторов, где декоратор возвращает новый объект, который выполняет повторную привязку имя класса:

@class_decorator_returning_new_class 
class Foo(Bar): 
    def baz(self): 
     # Now `Foo` is a *different class* 
     return super(Foo, self).baz() + 42 

Волшебные super()__class__ клетки уклоняются эти вопросы хорошо, давая доступ к исходному объекту класса.

PEP был выпущен Guido, который initially envisioned super becoming a keyword, и идея использования ячейки для поиска текущего класса was also his. Конечно, идея сделать это ключевым словом была частью first draft of the PEP.

Однако, это был сам Гвидо, который затем stepped away from the keyword idea as 'too magical', предлагая вместо этого текущую реализацию. Он anticipated that using a different name for super() could be a problem:

Мой патч использует промежуточное решение: он предполагает, что вам нужно __class__ всякий раз, когда вы используете переменную с именем 'super'. Таким образом, если вы (глобально) переименовать super в supper и использовать supper но не super, он не будет работать без аргументов (но он все равно будет работать, если вы передадите его либо __class__ или фактический класс объекта); если у вас есть несвязанная переменная с именем super, все будет работать, но метод будет использовать медленный путь вызова , используемый для переменных ячейки.

Таким образом, в конце концов, это был сам Гвидо, что провозгласил, что с помощью ключевого слова super не чувствовал себя хорошо, и что предоставление волшебной __class__ клетки является приемлемым компромиссом.

Я согласен с тем, что магическое, неявное поведение реализации несколько удивительно, но super() является одной из самых неправильно применяемых функций на этом языке. Просто взгляните на все неправильные super(type(self), self) или super(self.__class__, self) приглашения, найденные в Интернете; если какой-либо из этого кода когда-либо вызывался из производного класса you'd end up with an infinite recursion exception. По меньшей мере упрощенный вызов super() без аргументов позволяет избежать , что проблема.

Что касается переименованного super_; просто ссылка __class__ в вашем методе также, и он снова будет работать. Ячейка создается, если вы ссылаетесь либо superили__class__ имен в методе:

>>> super_ = super 
>>> class A(object): 
...  def x(self): 
...   print("No flipping") 
... 
>>> class B(A): 
...  def x(self): 
...   __class__ # just referencing it is enough 
...   super_().x() 
... 
>>> B().x() 
No flipping 
+1

Хорошая рецензия. Это все еще так ясно, как грязь. Вы говорите, что super() эквивалентно автоматически созданной функции, например 'def super (of_class = magic __class __)' вроде как 'self.super(); def super (сам): возврат самостоятельно .__ класс__'? –

+12

@CharlesMerriam: Это сообщение не о том, как работает 'super()' без аргументов; он в основном имеет дело с * почему * он существует. 'super()', в методе класса, эквивалентно 'super (ReferenceToClassMethodIsBeingDefinedIn, self)', где 'ReferenceToClassMethodIsBeingDefinedIn' определяется во время компиляции, прикрепляется к методу как закрытие с именем '__class__' и' super() 'будет искать оба из вызывающего кадра во время выполнения. Но на самом деле вам не нужно все это знать. –

+1

@CharlesMerriam: но 'super()' нигде не близок к тому, чтобы быть автоматически созданной функцией *, нет. –