Python: argumento específico del decorador (no relacionado con la función envuelta)?

StackOverflow https://stackoverflow.com/questions/660727

  •  20-08-2019
  •  | 
  •  

Pregunta

Estoy buscando construir un decorador de almacenamiento en caché que, dada una función, almacena en caché el resultado de la función en una ubicación especificada en la decoración. Algo como esto:

@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 estoy entendiendo cómo hacer esto: ¿es posible tener un argumento para el decorador que no esté relacionado y que no se pase a la función envuelta?

¿Fue útil?

Solución

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

PRIMERO Escribe tu decorador como si supieras que tu argumento es 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 Escribe una función que tome cachepath como argumento y devuelva tu 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

Otros consejos

Sí lo es. Como sabes, un decorador es una función. Cuando se escribe en el formulario:

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 en sí misma.

Cuando el decorador acepta un argumento, la llamada @mydecorator('/path/to') realmente llamará a la función mydecorator con '/ path / to' primero. Luego se llamará al resultado de la llamada a mydecorator(path) para recibir la función <=>. Estás definiendo efectivamente una función de envoltura dinámica.

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

Aquí está este ejemplo un poco tonto:

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 respuesta de Paul es buena, movería el objeto de caché para que no sea necesario construirlo todo el tiempo, y diseñaría su caché para que genere KeyError cuando falte el 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
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top