Perché la super () magia di Python 3.x?


159

In Python 3.x, super()può essere chiamato senza argomenti:

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

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

Al fine di rendere questo lavoro, un po 'di magia in fase di compilazione viene eseguita, una conseguenza di ciò è che il seguente codice (che rebinds supera super_) non riesce:

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

Perché super()non è possibile risolvere la superclasse in fase di esecuzione senza l'assistenza del compilatore? Ci sono situazioni pratiche in cui questo comportamento, o la ragione sottostante, potrebbe mordere un programmatore inconsapevole?

... e, come domanda secondaria: ci sono altri esempi in Python di funzioni, metodi ecc. che possono essere infranti riconducendoli a un nome diverso?


6
Ti lascio Armin fare il che spiega su questo uno . Questo è anche un altro buon post
Games Brainiac,

Risposte:


217

Il nuovo super()comportamento magico è stato aggiunto per evitare di violare il principio DRY (Don't Repeat Yourself), vedi PEP 3135 . Dover nominare esplicitamente la classe facendo riferimento a essa come globale è anche soggetto agli stessi problemi di rebinding che hai scoperto con super()se stesso:

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

Spam = Foo
Foo = something_else()

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

Lo stesso vale per l'utilizzo di decoratori di classi in cui il decoratore restituisce un nuovo oggetto, che ribattezza il nome della classe:

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

La super() __class__cellula magica elimina bene questi problemi dandoti l'accesso all'oggetto classe originale.

Il PEP è stato lanciato da Guido, che inizialmente aveva previsto di superdiventare una parola chiave , e l'idea di usare una cella per cercare la classe attuale era anche sua . Certamente, l'idea di farne una parola chiave faceva parte della prima bozza del PEP .

Tuttavia, è stato in effetti lo stesso Guido a rinunciare all'idea della parola chiave come "troppo magico" , proponendo invece l'attuale implementazione. Ha anticipato che l'uso di un nome diverso per super()potrebbe essere un problema :

La mia patch usa una soluzione intermedia: presume che tu abbia bisogno __class__ ogni volta che usi una variabile chiamata 'super'. Pertanto, se (globalmente) rinomina superper suppere uso supperma non super, non funzionerà senza argomenti (ma continuerà a funzionare se si passa sia __class__o all'oggetto classe reale); se hai una variabile non correlata chiamata super, le cose funzioneranno ma il metodo utilizzerà il percorso di chiamata leggermente più lento usato per le variabili di cella.

Quindi, alla fine, fu lo stesso Guido a proclamare che l'uso di una superparola chiave non sembrava corretto e che fornire una __class__cellula magica era un compromesso accettabile.

Concordo sul fatto che il comportamento magico e implicito dell'implementazione sia in qualche modo sorprendente, ma super()sia una delle funzioni più erroneamente applicate nel linguaggio. Dai un'occhiata a tutte le applicazioni errate super(type(self), self)o super(self.__class__, self) invocate trovate su Internet; se qualcuno di quel codice fosse mai stato chiamato da una classe derivata si finirebbe con un'eccezione di ricorsione infinita . Per lo meno la super()chiamata semplificata , senza argomenti, evita quel problema.

Per quanto riguarda il rinominato super_; appena riferimento __class__nel metodo così e che funzionerà di nuovo. La cella viene creata se si fa riferimento ai nomi super o __class__ nel metodo:

>>> 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
Buona scrittura. È comunque chiaro come il fango. Stai dicendo che super () equivale a una funzione istanziata automaticamente come una def super(of_class=magic __class__)specie di self.super(); def super(self): return self.__class__?
Charles Merriam,

17
@CharlesMerriam: questo post non parla di come funziona super()senza argomenti; si occupa principalmente del perché esiste. super(), in un metodo di classe, equivale a super(ReferenceToClassMethodIsBeingDefinedIn, self), dove ReferenceToClassMethodIsBeingDefinedInviene determinato in fase di compilazione, associato al metodo come una chiusura denominata __class__e super()cercherà entrambi dal frame chiamante in fase di esecuzione. Ma in realtà non è necessario sapere tutto questo.
Martijn Pieters

1
@CharlesMerriam: ma super()non è affatto vicino all'essere una funzione istanziata automaticamente , no.
Martijn Pieters

1
@ chris.leonard: la frase chiave è La cella viene creata se usi super () o usi __class__nel tuo metodo . Hai usato il nome supernella tua funzione. Il compilatore lo vede e aggiunge la __class__chiusura.
Martijn Pieters

4
@Alexey: è non è sufficiente. type(self)fornisce il tipo corrente , che non è lo stesso del tipo su cui è definito il metodo. Quindi una classe Foocon metodo ha bazbisogno super(Foo, self).baz(), perché potrebbe essere sottoclassata come class Ham(Foo):, a quel punto type(self)è Hame super(type(self), self).baz()ti darebbe un ciclo infinito. Vedi il post a cui mi collego
Martijn Pieters
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.