2009-03-19 5 views
6

Sto cercando di creare un decoratore di memorizzazione nella cache che, data una funzione, memorizza il risultato della funzione nella posizione specificata nella decorazione. Qualcosa del genere:Python: argomento specifico del decoratore (non correlato alla funzione di avvolgimento)?

@cacheable('/path/to/cache/file') 
def my_function(a, b, c): 
    return 'something' 

L'argomento per il decoratore è completamente separato dall'argomento della funzione che esso esegue. Ho esaminato alcuni esempi, ma non riesco a capire come farlo: è possibile avere un argomento per il decoratore non correlato e non passato alla funzione wrapping?

risposta

9

L'idea è che il decoratore è una funzione che restituisce un decoratore.

FIRST Scrivi il tuo decoratore come se sapessi che il tuo argomento era una variabile globale. Diciamo qualcosa di simile:

-

def decorator(f): 
    def decorated(*args,**kwargs): 
     cache = Cache(cachepath) 
     if cache.iscached(*args,**kwargs): 
      ... 
     else: 
      res = f(*args,**kwargs) 
      cache.store((*args,**kwargs), res) 
      return res 
    return decorated 

POI scrivere una funzione che prende CachePath come arg e restituire il decoratore.

-

def cache(filepath) 
    def decorator(f): 
     def decorated(*args,**kwargs): 
      cache = Cache(cachepath) 
      if cache.iscached(*args,**kwargs): 
       ... 
      else: 
       res = f(*args,**kwargs) 
       cache.store((*args,**kwargs), res) 
       return res 
     return decorated 
    return decorator 
5

Sì, lo è. Come sai, un decoratore è una funzione. Quando scritta nella forma:

def mydecorator(func): 
    def wrapper(*args, **kwargs): 
     return func(*args, **kwargs) 
    return wrapper 

@mydecorator 
def foo(a, b, c): 
    pass 

l'argomento passato alla mydecorator è la funzione foo stesso.

Quando il decoratore accetta un argomento, la chiamata @mydecorator('/path/to') chiama in realtà la funzione mydecorator con '/ percorso/a' prima. Quindi il risultato della chiamata a mydecorator(path) verrà chiamato per ricevere la funzione foo. Stai definendo in modo efficace una funzione di wrapper dinamico.

In breve, è necessario un altro livello di funzioni di decoratore.

Ecco questo esempio un po 'sciocco:

def addint(val): 
    def decorator(func): 
     def wrapped(*args, **kwargs): 
      result = func(*args, **kwargs) 
      return result + val 
     return wrapped # returns the decorated function "add_together" 
    return decorator # returns the definition of the decorator "addint" 
         # specifically built to return an extra 5 to the sum 

@addint(5) 
def add_together(a, b): 
    return a + b 

print add_together(1, 2) 
# prints 8, not 3 
3

risposta di Paolo è buono, vorrei spostare l'oggetto della cache in modo che non ha bisogno di essere costruita ogni volta, e progettare la cache in modo che solleva KeyError in caso di mancanza della cache:

def cache(filepath): 
    def decorator(f): 
     f._cache = Cache(cachepath) 
     def decorated(*args,**kwargs): 
      try: 
       key = (args, kwargs) 
       res = f._cache.get(key) 
      except KeyError: 
       res = f(*args, **kwargs) 
       f._cache.put(key, res) 
      return res 
     return decorated 
    return decorator 
Problemi correlati