Python: argomento specifico del decoratore (non correlato alla funzione wrapping)?
Domanda
Sto cercando di creare un decoratore di memorizzazione nella cache che, data una funzione, memorizza nella cache il risultato della funzione in una posizione specificata nella decorazione. Qualcosa del genere:
@cacheable('/path/to/cache/file')
def my_function(a, b, c):
return 'something'
L'argomento per il decoratore è completamente separato dall'argomento per la funzione che sta avvolgendo. Ho esaminato alcuni esempi, ma non riesco proprio a capire come farlo - è possibile avere un argomento per il decoratore che non è correlato e non è passato alla funzione wrapped?
Soluzione
L'idea è che il tuo decoratore sia una funzione che restituisce un decoratore.
PRIMO Scrivi il tuo decoratore come se sapessi che il tuo argomento era una variabile globale. Diciamo qualcosa del tipo:
-
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 Scrivi una funzione che prende cachepath come arg e restituisce il tuo 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
Altri suggerimenti
Sì, lo è. Come sai, un decoratore è una funzione. Se scritto nel modulo:
def mydecorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
@mydecorator
def foo(a, b, c):
pass
l'argomento passato a mydecorator
è la funzione foo
stessa.
Quando il decoratore accetta un argomento, la chiamata @mydecorator('/path/to')
in realtà chiamerà prima la funzione mydecorator con '/ path / to'. Quindi il risultato della chiamata a mydecorator(path)
verrà chiamato per ricevere la funzione <=>. Stai effettivamente definendo una funzione di wrapper dinamico.
In poche parole, hai bisogno di un altro livello di funzioni di decorazione.
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
La risposta di Paul è buona, sposterei l'oggetto cache in modo che non debba essere creato ogni volta, e progetterei la tua cache in modo che aumenti 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