Почему 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 133

1 ответ:

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

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__ ячейка обходит эти проблемы красиво, предоставляя вам доступ к исходному объекту класса.

бодрость духа был сброшен Гвидо, который первоначально envisioned super становится ключевым словом, и идея использования ячейки для поиска текущего класса и. Конечно, идея сделать его ключевым словом была частью первый проект ОПТОСОЗ.

однако на самом деле это был сам Гвидо, который тогда отошел от ключевое слово идея как "слишком волшебный", предлагая вместо этого текущую реализацию. Он ожидал, что использование другого имени для 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