2009-03-19 7 views
6

Je cherche à construire un décorateur de mise en cache qui, avec une fonction, met en cache le résultat de la fonction à un emplacement spécifié dans la décoration. Quelque chose comme ceci:Python: argument spécifique du décorateur (non lié à la fonction enveloppée)?

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

L'argument au décorateur est complètement séparé de l'argument à la fonction qu'il est d'emballage. J'ai regardé un certain nombre d'exemples mais je ne comprends pas comment faire ceci - est-il possible d'avoir un argument pour le décorateur qui n'est pas lié à et n'est pas passé à la fonction enveloppée?

Répondre

9

L'idée est que votre décorateur est une fonction de retour d'un décorateur.

PREMIÈREMENT Écrivez votre décorateur comme si vous saviez que votre argument était une variable globale. Disons que quelque chose comme:

-

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 

ALORS Ecrire une fonction qui prend CachePath comme un arg et retourner votre décorateur.

-

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

Oui il est. Comme vous le savez, un décorateur est une fonction. Lorsque écrit sous la forme:

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

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

l'argument passé à mydecorator est la fonction foo lui-même. Lorsque le décorateur accepte un argument, l'appel @mydecorator('/path/to') appelle en fait la fonction mydecorator avec '/ path/to' en premier. Ensuite, le résultat de l'appel à mydecorator(path) sera appelé pour recevoir la fonction foo. Vous définissez effectivement une fonction d'encapsuleur dynamique.

En un mot, vous avez besoin d'une autre couche de fonctions de décorateur.

Voici cet exemple un peu stupide:

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

La réponse de Paul est bon, je proposerais l'objet de cache de sorte qu'il n'a pas besoin d'être construit à chaque fois, et la conception de votre cache afin qu'il soulève KeyError quand il y a une missive 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 
Questions connexes