2012-05-16 9 views
9

Hat die Python-Standardbibliothek eine Verknüpfung zum Schreiben von Dekoratoren, die Argumente akzeptieren?Python: Verknüpfung zum Schreiben von Dekoratoren, die Argumente akzeptieren?

Zum Beispiel, wenn ich einen Dekorateur wie with_timeout(timeout) schreiben will:

@with_timeout(10.0) 
def cook_eggs(eggs): 
    while not eggs.are_done(): 
     eggs.cook() 

Ich habe zu schreiben, so etwas wie:

def with_timeout(timeout): 
    _func = [None] 
    def with_timeout_helper(*args, **kwargs): 
     with Timeout(timeout): 
      return _func[0](*args, **kwargs) 
    def with_timeout_return(f): 
     return functools.wraps(f)(with_timeout_helper) 
    return with_timeout_return 

Aber das ist schrecklich ausführlich. Gibt es eine Abkürzung, die Dekoratoren, die Argumente akzeptieren, leichter schreiben lässt?

Hinweis: Ich weiß, dass es auch möglich ist, drei verschachtelte Funktionen zu verwenden, Dekorateure mit Argumenten zu implementieren ... Aber das fühlt sich einfach ein bisschen zu suboptimal.

Zum Beispiel, vielleicht so etwas wie eine @decorator_with_arguments Funktion:

@decorator_with_arguments 
def timeout(f, timeout): 
    @functools.wraps(f) 
    def timeout_helper(*args, **kwargs): 
     with Timeout(timeout): 
      return f(*args, **kwargs) 
    return timeout_helper 
+0

Wenn Sie mehr Hilfe benötigen Dekoratoren und Anmerkungen finden Sie in meinem Blog hier. http://blog.mattalcock.com/2013/1/5/decorates-and-annotations/ –

Antwort

7

Ich neige dazu, meine Dekorateure zu schreiben, wie Klassen ehrlich zu sein

class TestWithArgs(object): 
    def __init__(self, *deco_args, **deco_kwargs): 
     self.deco_args = deco_args 
     self.deco_kwargs = deco_kwargs 
    def __call__(self, func): 
     def _wrap(self, *args, **kwargs): 
      print "Blah blah blah" 
      return func(*args, **kwargs) 
     return _wrap 

Sein nichts, wenn nicht etwas klarer

4

I Ich weiß, du hast gesagt, es fühlt sich suboptimal an, aber ich habe immer noch das Gefühl, dass die Verwendung von drei verschachtelten Modellen die sauberste Lösung ist. Die inneren zwei Funktionen sind nur die "normale" Art, einen Dekorator für eine Funktion zu definieren, die Argumente übernimmt (siehe example in Pythons Dokumenten für @wraps). Der äußere ist wirklich nur eine Funktion, die einen Dekorator übernimmt und zurückgibt.

def with_timeout(timeout): 
    def decorator(f): 
     @wraps(f) 
     def wrapper(*args, **kwargs): 
      with Timeout(timeout): 
       return f(*args, **kwargs) 
     return wrapper 
    return decorator 
+0

Sorry, aber ich stimme nicht zu. Über drei verschachtelte Funktionen (oder, so wie ich es tue) gibt es überhaupt nichts sauberes. Es ist ein Hack. Besser wäre zum Beispiel ein '@ decorator_with_arguments'-Dekorator (siehe meine aktualisierte Frage). –

4

Basierend auf Jakobs Vorschlag, habe ich eine kleine Decorator Klasse implementiert, die ich eine ziemlich gute Arbeit fühlt sich:

class Decorator(object): 
    def __call__(self, f): 
     self.f = f 
     return functools.wraps(f)(lambda *a, **kw: self.wrap(*a, **kw)) 

    def wrap(self, *args, **kwrags): 
     raise NotImplemented("Subclasses of Decorator must implement 'wrap'") 

class with_timeout(Decorator): 
    def __init__(self, timeout): 
     self.timeout = timeout 

    def wrap(self, *args, **kwargs): 
     with Timeout(timeout): 
      return self.f(*args, **kwargs) 
+1

Ich habe einen Tippfehler auf Ihrer 'with_timeout' Methode' warp' behoben –

+0

D'oh! Vielen Dank. Das ist wahrscheinlich das achte Mal, dass ich heute Nacht 'Warp' statt 'Wrap' getippt habe. Und ich habe das nicht oft genug getippt. –

0

Erstens haben wir ein wenig Meta-Dekorateur definieren:

def decorator_with_arguments(wrapper): 
    return lambda *args, **kwargs: lambda func: wrapper(func, *args, **kwargs) 

das führt uns Dekorateure erstellen können, die Argumente wie so akzeptieren:

@decorator_with_arguments 
def my_wrapper(func, *decorator_args, **decorator_kwargs): 
    def wrapped(*call_args, **call_kwargs): 
     print "from decorator:", decorator_args, decorator_kwargs 
     func(*call_args, **call_kwargs) 
    return wrapped 

die dann wie gewohnt verwendet werden:

@my_wrapper(1, 2, 3) 
def test(*args, **kwargs): 
    print "passed directly:", args, kwargs 

test(4, 5, 6) 

functools.wraps Dekoration Hinzufügen wird als Übung :)

0

Eine andere nehmen, ohne die Verwendung von Lambda-Ausdrücke:

def decorator_with_arguments(f): 
    @functools.wraps(f) 
    def with_arguments_helper(*args, **kwargs): 
     def decorator(g): 
      return f(g, *args, **kwargs) 
     return decorator 
    return with_arguments_helper 
Verwandte Themen