2009-03-19 4 views
6

Estoy buscando construir un decorador de caché que, con una función, almacene el resultado de la función en una ubicación especificada en la decoración. Algo como esto:Python: argumento específico del decorador (no relacionado con la función envuelta)?

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

El argumento para el decorador está completamente separado del argumento de la función que está envolviendo. He visto bastantes ejemplos, pero no entiendo muy bien cómo hacerlo: ¿es posible tener un argumento para el decorador que no esté relacionado y no haya pasado a la función envuelta?

Respuesta

9

La idea es que su decorador sea una función que devuelve un decorador.

FIRST Escriba su decorador como si supiera que su argumento era una variable global. Digamos algo como:

-

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 

ENTONCES escribir una función que toma CachePath como arg y regresar a su decorador.

-

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 es. Como sabes, un decorador es una función. Cuando se escribe en la forma:

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

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

el argumento pasado a mydecorator es la función foo sí.

Cuando el decorador acepta un argumento, la llamada @mydecorator('/path/to') realmente llamará primero a la función mydecorator con '/ path/to'. A continuación, se llamará al resultado de la llamada al mydecorator(path) para recibir la función foo. Definitivamente está definiendo una función de envoltura dinámica.

En pocas palabras, necesita otra capa de funciones de decorador.

Aquí es esto un poco tonta ejemplo:

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

respuesta de Pablo es buena, me gustaría mover el objeto de caché por lo que no tiene que ser construida cada vez, y el diseño de su caché para que se plantea KeyError cuando hay una falta de caché:

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 
Cuestiones relacionadas