2009-03-19 7 views
6

Ich bin auf der Suche nach einem Caching-Dekorator, der bei einer gegebenen Funktion das Ergebnis der Funktion an einem Ort speichert, der in der Dekoration angegeben ist. So etwas wie das:Python: dekoratorspezifisches Argument (nicht verwandt mit umgebrochener Funktion)?

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

Das Argument für den Dekorator ist vollständig getrennt von dem Argument für die Funktion, die es wickelt. Ich habe mir ein paar Beispiele angeschaut, aber ich verstehe nicht, wie ich das machen soll - ist es möglich, ein Argument für den Dekorator zu haben, das nichts damit zu tun hat und nicht an die eingepackte Funktion übergeben wird?

Antwort

9

Die Idee ist, dass Ihr Decorator eine Funktion ist, die einen Decorator zurückgibt.

FIRST Schreiben Sie Ihren Dekorateur, als wüssten Sie, dass Ihr Argument eine globale Variable ist. Lassen Sie uns etwas sagen wie:

-

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 

dann eine Funktion schreiben, die CachePath als arg nimmt und zurück Ihr Dekorateur.

-

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

Ja, es ist. Wie Sie wissen, ist ein Dekorateur eine Funktion. Wenn sie in Form geschrieben:

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

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

die zu mydecorator gebene Argument ist die Funktion foo selbst.

Wenn der Decorator ein Argument akzeptiert, ruft der Aufruf @mydecorator('/path/to') die Mydecorator-Funktion tatsächlich zuerst mit '/ Pfad/zu' auf. Dann wird das Ergebnis des Aufrufs an mydecorator(path) aufgerufen, um die Funktion foo zu empfangen. Sie definieren effektiv eine dynamische Wrapperfunktion.

Kurz gesagt, Sie brauchen eine weitere Schicht Dekorator-Funktionen.

Hier dieses leicht albern Beispiel:

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

Paul Antwort ist gut, würde ich das Cache-Objekt zu verschieben, so dass es jedes Mal gebaut braucht nicht zu, und gestalten Sie Ihre Cache, so dass es KeyError wirft Wenn ein Cache fehlt:

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 
Verwandte Themen