2013-10-26 4 views
125

In Python 3.x, super() può essere chiamato senza argomenti:Perché la magia super() di Python 3.x è eccellente?

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

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

Per fare questo lavoro, viene eseguita una certa magia in fase di compilazione, una conseguenza dei quali è che il seguente codice (che rebinds super a super_) fallisce:

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() in grado di res olve 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 incauto?

... e, come una domanda a parte: ci sono altri esempi in Python di funzioni, metodi ecc. Che possono essere interrotti accoppiandoli a un nome diverso?

+6

vi svelo Armin fare lo spiega in questo [uno] (http://lucumr.pocoo.org/2010/1/7/pro-e-contro-about-python-3 /). Anche questo è un altro buon [post] (http://lucumr.pocoo.org/2013/5/21/porting-to-python-3-redux/) –

+0

correlato: http://stackoverflow.com/q/36993577/674039 – wim

risposta

184

Il nuovo comportamento magico super() è stato aggiunto per evitare la violazione del D.R.Y. (Non ripetere te stesso) principio, vedi PEP 3135. Dovendo nominare esplicitamente la classe facendo riferimento a come globale è anche soggetta agli stessi problemi di rebinding si scoprì con super() 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 utilizzando decoratori di classe dove il decoratore restituisce un nuovo oggetto, che rebinds 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 magia delle cellule super()__class__ evita questi problemi piacevolmente dando accesso all'oggetto classe originale.

Il PEP è stato avviato da Guido, che è initially envisioned super becoming a keyword, e l'idea di utilizzare una cella per cercare la classe corrente was also his. Certamente, l'idea di farne una parola chiave faceva parte dello first draft of the PEP.

Tuttavia, era in realtà Guido stesso che quindi stepped away from the keyword idea as 'too magical', proponendo invece l'implementazione corrente. Egli anticipated that using a different name for super() could be a problem:

mia patch utilizza una soluzione intermedia: assume il necessario __class__ ogni volta che si utilizza una variabile denominata 'super'. Pertanto, se (globalmente) rinomina super a supper e utilizzare supper ma non super ma non funzionerà senza argomenti (ma continuerà a funzionare se si passa neanche __class__ o l'oggetto classe reale); se si dispone di una variabile non correlata denominata super, le cose funzioneranno ma il metodo utilizzerà il percorso di chiamata leggermente più lento utilizzato per le variabili di cella.

Così, alla fine, è stato Guido se stesso che ha proclamato che utilizzando una parola chiave super non si sentiva bene, e che fornire una magia delle cellule __class__ era un compromesso accettabile.

Sono d'accordo sul fatto che la magia, il comportamento implicito dell'implementazione è in qualche modo sorprendente, ma super() è una delle funzioni più male applicate nella lingua. Basta dare un'occhiata a tutte le chiamate errate super(type(self), self) o super(self.__class__, self) trovate su Internet; se qualcuno di quel codice è stato mai chiamato da una classe derivata you'd end up with an infinite recursion exception. Per lo meno la chiamata semplificata super(), senza argomenti, evita lo del problema.

Come per il nome super_; basta fare riferimento a __class__ nel metodo e e funzionerà di nuovo. La cella si crea se si fa riferimento sia alle supero__class__ nomi 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() è equivalente a una funzione istanziata automaticamente come 'def super (of_class = magic __class __)' un po 'come un 'self.super(); def super (self): return self .__ class__'? –

+12

@CharlesMerriam: questo post non tratta di come 'super()' senza argomenti funziona; si occupa principalmente del * perché * esiste. 'super()', in un metodo di classe, è equivalente a 'super (ReferenceToClassMethodIsBeingDefinedIn, self)', dove 'ReferenceToClassMethodIsBeingDefinedIn' è determinato al momento della compilazione, collegato al metodo come chiusura chiamato' __class__' e 'super() 'cercherà entrambi dal frame di chiamata in fase di esecuzione. Ma in realtà non hai bisogno di sapere tutto questo. –

+1

@CharlesMerriam: ma 'super()' non è affatto vicino ad essere una * funzione istanziata automaticamente *, no. –