Python: argument spécifique au décorateur (sans rapport avec la fonction encapsulée)?

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

  •  20-08-2019
  •  | 
  •  

Question

Je souhaite créer un décorateur de mise en cache qui attribue à une fonction le résultat de sa mise en cache à un emplacement spécifié dans la décoration. Quelque chose comme ça:

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

L'argument adressé au décorateur est complètement séparé de l'argument de la fonction qu'il enveloppe. J'ai regardé pas mal d'exemples, mais je ne comprends pas trop comment faire cela. Est-il possible d'avoir un argument pour le décorateur qui ne soit pas lié à la fonction encapsulée?

Était-ce utile?

La solution

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

PREMIER : écrivez votre décorateur comme si vous saviez que votre argument était une variable globale. Disons 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

THEN Écrivez une fonction qui prend cachepath en tant qu'argument et renvoie 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

Autres conseils

Oui c'est ça. Comme vous le savez, un décorateur est une fonction. Sous forme écrite:

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 elle-même.

Lorsque le décorateur accepte un argument, l'appel @mydecorator('/path/to') va en fait appeler la fonction mydecorator avec '/ path / to' en premier. Ensuite, le résultat de l'appel à mydecorator(path) sera appelé pour recevoir la fonction <=>. Vous définissez effectivement une fonction de wrapper dynamique.

En résumé, vous avez besoin d'une autre couche de fonctions de décorateur.

Voici cet exemple légèrement idiot:

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 réponse de Paul est bonne, je déplacerais l'objet cache pour qu'il n'ait pas besoin d'être construit à chaque fois, et concevrais votre cache de manière à ce qu'il lève KeyError en cas d'erreur de 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
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top