Dlaczego super () magia Pythona 3.x?

159

W Pythonie 3.x super()można wywołać bez argumentów:

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

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

Aby to zadziałało, wykonywana jest pewna magia w czasie kompilacji, której jedną z konsekwencji jest to, że następujący kod (który ponownie supersię wiąże super_) zawodzi:

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

Dlaczego super()nie można rozwiązać nadklasy w czasie wykonywania bez pomocy kompilatora? Czy istnieją praktyczne sytuacje, w których takie zachowanie lub jego przyczyna mogą ugryźć nieostrożnego programistę?

... i na marginesie: czy w Pythonie są jakieś inne przykłady funkcji, metod itp., które można zepsuć, przywiązując je do innej nazwy?

Zero Piraeus
źródło
6
Powiem Armin zrobić tłumacząc na tej jednej . To także kolejny dobry post
Games Brainiac

Odpowiedzi:

217

super()Dodano nowe magiczne zachowanie, aby uniknąć naruszenia zasady SUCHY (nie powtarzaj się), patrz PEP 3135 . Konieczność jawnego nazwania klasy przez odwołanie się do niej jako globalnej jest również podatna na te same problemy z ponownym wiązaniem, które odkryłeś z super()samą sobą:

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

Spam = Foo
Foo = something_else()

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

To samo dotyczy używania dekoratorów klas, w których dekorator zwraca nowy obiekt, który ponownie wiąże nazwę klasy:

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

Magiczna super() __class__komórka ładnie omija te problemy, dając ci dostęp do oryginalnego obiektu klasy.

PEP został zapoczątkowany przez Guido, który początkowo wyobrażał sobie, że superzostanie słowem kluczowym , a pomysł wykorzystania komórki do wyszukania aktualnej klasy był również jego . Z pewnością pomysł uczynienia go słowem kluczowym był częścią pierwszego projektu PEP .

Jednak w rzeczywistości to sam Guido odszedł od pomysłu słowa kluczowego jako „zbyt magiczny” , proponując zamiast tego obecną implementację. On Przewiduje się, że przy użyciu innej nazwy super()może być problem :

Moja łatka używa rozwiązania pośredniego: zakłada, że ​​potrzebujesz, __class__ gdy używasz zmiennej o nazwie 'super'. Tak więc, jeśli (globalnie) zmienisz nazwę superna supperi użyjesz, supperale nie super, to nie będzie działać bez argumentów (ale nadal będzie działać, jeśli przekażesz go __class__lub rzeczywisty obiekt klasy); jeśli masz nazwaną niepowiązaną zmienną super, wszystko będzie działać, ale metoda użyje nieco wolniejszej ścieżki wywołania używanej dla zmiennych komórki.

W końcu to sam Guido ogłosił, że użycie supersłowa kluczowego nie wydaje się właściwe, a zapewnienie magicznej __class__komórki jest akceptowalnym kompromisem.

Zgadzam się, że magiczne, ukryte zachowanie implementacji jest nieco zaskakujące, ale super()jest jedną z najczęściej niewłaściwie stosowanych funkcji w języku. Wystarczy spojrzeć na wszystkie niewłaściwie zastosowane super(type(self), self)lub super(self.__class__, self) inwokacje znalezione w Internecie; gdyby którykolwiek z tych kodów był kiedykolwiek wywołany z klasy pochodnej , skończyłoby się to z nieskończonym wyjątkiem rekursji . Przynajmniej uproszczone super()wywołanie, bez argumentów, pozwala uniknąć tego problemu.

Co do przemianowanych super_; wystarczy odniesienie __class__w sposobie , jak również i to będzie działać ponownie. Komórka jest tworzona, jeśli odwołasz się do nazwy super lub __class__ w swojej metodzie:

>>> 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
Martijn Pieters
źródło
1
Dobry opis. Jednak nadal jest tak czysty jak błoto. Mówisz, że super () jest odpowiednikiem automatycznie tworzonej funkcji, takiej jak def super(of_class=magic __class__)coś w rodzaju self.super(); def super(self): return self.__class__?
Charles Merriam
17
@CharlesMerriam: Ten post nie dotyczy tego, jak super()działa bez argumentów; zajmuje się głównie tym, dlaczego istnieje. super(), w metodzie klasy, jest równoważne z super(ReferenceToClassMethodIsBeingDefinedIn, self), gdzie ReferenceToClassMethodIsBeingDefinedInjest określane w czasie kompilacji, dołączone do metody jako o nazwie zamknięcie __class__i super()wyszuka oba elementy z wywołującej ramki w czasie wykonywania. Ale tak naprawdę nie musisz tego wszystkiego wiedzieć.
Martijn Pieters
1
@CharlesMerriam: ale super()nigdzie nie jest to funkcja automatycznie tworzona , nie.
Martijn Pieters
1
@ chris.leonard: kluczowe zdanie brzmi : Komórka jest tworzona, jeśli używasz super () lub używasz __class__w swojej metodzie . Użyłeś nazwy superw swojej funkcji. Kompilator widzi to i dodaje __class__zamknięcie.
Martijn Pieters
4
@Alexey: to nie wystarczy. type(self)podaje bieżący typ, który nie jest tym samym, co typ, dla którego zdefiniowano metodę. Tak więc klasa Fooz metodą bazpotrzebuje super(Foo, self).baz(), ponieważ może być podklasa class Ham(Foo):, w którym momencie type(self)jest Hami super(type(self), self).baz()daje nieskończoną pętlę. Zobacz post, do którego odsyłam w mojej odpowiedzi: Czy podczas wywoływania super () w klasie pochodnej mogę przekazać self .__ class__?
Martijn Pieters