Чому супер () магія Python 3.x?


159

У 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 інші приклади функцій, методів тощо, які можна порушити, перезаписавши їх на іншу назву?


6
Я дам Armin зробити пояснення з цього одному . Це ще один хороший пост
Games Brainiac

Відповіді:


217

Додано нову магічну super()поведінку, щоб уникнути порушення принципу DRY (не повторюй себе), див. 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 був розпочатий Гвідо, який спочатку передбачав superстати ключовим словом , і ідея використання комірки для пошуку поточного класу також була його . Звичайно, ідея зробити його ключовим словом була частиною першого проекту ПЕП .

Однак насправді був саме Гуїдо відмовився від ідеї ключових слів як "занадто магічної" , запропонувавши замість цього поточну реалізацію. Він припускав, що використання іншої назви super()може бути проблемою :

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

Отже, врешті-решт, саме Гвідо проголосив, що використовуючи a 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

1
Гарне оформлення. Це все ще так само ясно, як грязь. Ви говорите, що super () еквівалентно автоматично створеній функції на зразок def super(of_class=magic __class__)типу self.super(); def super(self): return self.__class__?
Чарльз Мерріам

17
@CharlesMerriam: Цей пост не про те, як super()працює без аргументів; вона в основному стосується того, чому вона існує. super(), у класному методі, еквівалентний тому super(ReferenceToClassMethodIsBeingDefinedIn, self), де ReferenceToClassMethodIsBeingDefinedInвизначається під час компіляції, додається до методу у вигляді імені закриття __class__, і super()під час виконання буде шукати обидва з виклику кадру. Але вам насправді не потрібно все це знати.
Martijn Pieters

1
@CharlesMerriam: але super()це ніде не є функцією автоматичної інстанції , ні.
Martijn Pieters

1
@ chris.leonard: ключове речення - Осередок створюється, якщо ви використовуєте super () або використовуєте __class__у своєму методі . Ви використовували ім’я superу своїй функції. Компілятор бачить це і додає __class__закриття.
Мартійн Пітерс

4
@Alexey: це НЕ достатньо. type(self)надає поточний тип, який не є типом типу, для якого визначено метод. Отже, клас Fooз методом bazпотребує super(Foo, self).baz(), тому що він може бути підкласифікований як class Ham(Foo):, в який момент type(self)є Hamі super(type(self), self).baz()дасть вам нескінченний цикл. Дивіться публікацію, на яку я посилаюсь у своїй відповіді: Коли я телефоную super () у похідному класі, чи можу я перейти до самостійного .__ class__?
Martijn Pieters
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.