2014-06-09 9 views
6
def decorator(fn): 
    def wrapper(*args, **kwargs): 
     print 'With sour cream and chives!', 
     return fn(*args, **kwargs) 
    return wrapper 

class Potato(object): 
    def __call__(self): 
     print 'Potato @ {} called'.format(id(self)) 

spud = Potato() 
fancy_spud = decorator(Potato()) 

Con questo codice abbiamo due istanze della classe callable, uno è decorata e uno è semplice:Come posso decorare un'istanza di una classe richiamabile?

>>> spud() 
Potato @ 140408136280592 called 
>>> fancy_spud() 
With sour cream and chives! Potato @ 140408134310864 called 

mi chiedo se è in qualche modo supportato per utilizzare la sintassi @decorator su un callable per un solo esempio, - al contrario di decorare la classe/metodo, che si applicherebbe ad ogni istanza. Secondo this risposta popolare, il @syntax è solo zucchero per:

function = decorator(function) 

Ma è una semplificazione eccessiva? Con tutti i miei tentativi a metà forno sembra funzionare solo quando la sintassi si verifica prima di def, class, spazio bianco o @another_decorator.

@decorator 
baked = Potato() 

Questo è un SyntaxError.

baked = Potato() 
@decorator 
baked 

SyntaxError.

@decorator 
def baked(_spud=Potato()): 
    return _spud() 

Funziona, ma è brutto e un pò barare.

risposta

3

È in discussione afferma:

Secondo questa risposta popolare, la @syntax è solo zucchero per:

function = decorator(function)

Tuttavia, è più esatto dire che

@decorator 
def function(): 
    pass 

È zucchero sintattico per:

def function(): 
    pass 
function = decorator(function) 

pittura sono progettati per decorare funzione, un metodo o una classe definizioni, specificamente. Il PEP that introduced class decorators descrive la grammatica:

decorated: decorators (classdef | funcdef) 

funcdef: 'def' NAME parameters ['->' test] ':' suite 

Come si può vedere, un decoratore deve venire immediatamente prima di un classdef o funcdef, quindi non c'è modo di utilizzarlo direttamente su un'istanza di una classe callable.

4

Sì, è una semplificazione eccessiva.Se guardiamo the grammar, decorator appare solo nella regola decorators, che appare solo come parte di un classdef o funcdef:

decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE 
decorators: decorator+ 
decorated: decorators (classdef | funcdef) 

Che language reference says (e credo che questo è ciò che si ripete nella risposta collegati) è che

@f1(arg) 
@f2 
def func(): pass 

è equivalente a

def func(): pass 
func = f1(arg)(f2(func)) 

e allo stesso modo per le definizioni di classe. Ma ciò non significa che la sintassi @decorator possa essere applicata ovunque; è valido solo immediatamente prima di una funzione o di una definizione di classe.

Per inciso, anche i documenti ufficiali non sono corretti; nel momento in cui il decoratore viene chiamato, la funzione (o classe) non è vincolata nello spazio dei nomi o nell'oscilloscopio, quindi le sintassi date non sono del tutto equivalenti.

C'è qualcosa di interessante circa i def e class affermazioni, che credo sia una delle ragioni che sono le uniche dichiarazioni supportate da @decorator sintassi: sono l'unico modo in Python per associare un nome a un oggetto che sa quale nome è.

Infine, ecco un altro modo per richiamare un decoratore che ti avrebbe fatto piacere:

@decorator 
class baked: 
    __metaclass__ = lambda *_: Potato() 
Problemi correlati