Почему в Python 3.x супер () магия?
В 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()
было добавлено поведение, чтобы избежать нарушения принципа СУХОЙ (не повторяй себя), см. 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__
клетка прекрасно обходит эти проблемы, предоставляя вам доступ к исходному объекту класса.
ПКП был начат Гвидо, который изначально предполагал 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