2016-02-10 23 views
7

Ho cercato di capire come funziona una certa implementazione di un decoratore singleton per una classe, ma sono solo confuso.Come i decoratori lavorano con le classi in python

Ecco il codice:

def singleton(cls): 
    instance = None 

    @functools.wraps(cls) 
    def inner(*args, **kwargs): 
     nonlocal instance 
     if instance is None: 
      instance = cls(*args, **kwargs) 
     return instance 
    return inner 

@deco è uno zucchero syntatic per cls = deco(cls), quindi in questo codice, quando definiamo la nostra classe cls e avvolgerlo con questo singleton decoratore, cls sarà non essere più una classe , ma una funzione. Python cerca in modo dinamico per quali oggetti sono collegate le variabili, quindi in seguito proveremo a creare un'istanza della nostra classe, e questa linea di codice gira instance = cls(*args, **kwargs), non andremo in una ricorsione infinita? cls non è una classe in questo momento, è una funzione, quindi dovrebbe chiamarsi, andare in ricorsione.

Ma funziona correttamente. Viene creato un singletone e non si verificano ricorsioni. Come funziona?

risposta

4

La funzione inner viene chiusa sulla variabile locale cls.

cls è un riferimento alla classe. Non è mai rimbalzo per nient'altro.

Il decoratore restituisce una funzione che restituisce un'istanza, ma questo non pregiudica quello che la variabile interna cls si riferisce a

3

cls è un riferimento alla classe originale passata al decoratore. Conserva il valore che aveva quando è stato chiamato il decoratore. Il suo valore è "intrappolato" nella funzione restituita dal decoratore; per ragioni oscure, questa è chiamata chiusura . La maggior parte delle lingue in cui le funzioni sono oggetti di prima classe hanno questa capacità.

2
def singleton(cls): 
    instance = None 

    @functools.wraps(cls) 
    def inner(*args, **kwargs): 
     nonlocal instance 
     if instance is None: 
      instance = cls(*args, **kwargs) 
      print cls 
     return instance 
    return inner 

@singleton 
class TryMe(object): 
    pass 

m = TryMe() 
print m 

Otterrete:

<class '__main__.TryMe'> 
<__main__.TryMe object at 0x10231c9d0>