В Python 3.x super()
можно вызывать без аргументов:
class A(object):
def x(self):
print("Hey now")
class B(A):
def x(self):
super().x()
>>> B().x()
Hey now
Для выполнения этой работы выполняется некоторая магия времени компиляции, одним из следствий которой является то, что следующий код (который восстанавливает от super
до super_
) сбой:
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 другие примеры функций, методов и т.д., которые можно сломать, перебирая их на другое имя?
Ответ 1
Новое магическое поведение 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, который изначально предполагал super
стать ключевым словом, а идея использования ячейки для поиска текущий класс также был его. Конечно, идея сделать это ключевым словом была частью первого черновика PEP.
Однако на самом деле сам Гвидо, который затем , предложив вместо этого, Он ожидал, что использование другого имени для super()
может быть проблемой:
Мой патч использует промежуточное решение: он предполагает, что вам нужно __class__
всякий раз, когда вы используете переменную с именем 'super'
. Таким образом, если вы (глобально) переименуйте super
в supper
и используйте supper
, но не super
, это не сработает без аргументов (но он все равно будет работать, если вы передадите его либо __class__
или фактический объект класса); если у вас есть несвязанный переменная с именем super
, все будет работать, но метод будет использовать немного более медленный путь вызова, используемый для переменных ячейки.
Итак, в конце концов, именно сам Гидо заявил, что использование ключевого слова super
не соответствует действительности, и что предоставление волшебной ячейки __class__
было приемлемым компромиссом.
Я согласен с тем, что магическое, неявное поведение реализации несколько удивительно, но super()
является одной из самых неправильно применяемых функций на языке. Просто взгляните на все непринятые super(type(self), self)
или super(self.__class__, self)
, обнаруженных в Интернете; если бы какой-либо из этого кода когда-либо вызывался из производного класса у вас получилось бы исключение бесконечной рекурсии. По меньшей мере упрощенный вызов 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