Frage

Wenn ich die folgende Funktion:


def intercept(func):
  # do something here

@intercept(arg1=20)
def whatever(arg1,arg2):
  # do something here

Ich möchte für Intercept nur feuern, wenn arg1 20. ist ich in der Lage sein möchte benannte Parameter an die Funktion übergeben. Wie könnte ich das erreichen?

Hier ist ein kleines Codebeispiel:



def intercept(func):
    def intercepting_func(*args,**kargs):
        print "whatever"
        return func(*args,**kargs)
    return intercepting_func

@intercept(a="g")
def test(a,b):
    print "test with %s %s" %(a,b)

test("g","d")

Dies wirft die folgende Ausnahme Typeerror: abfangen () bekam ein unerwartetes Stichwort Argument 'a'

War es hilfreich?

Lösung

from functools import wraps

def intercept(target,**trigger):
    def decorator(func):
        names = getattr(func,'_names',None)
        if names is None:
            code = func.func_code
            names = code.co_varnames[:code.co_argcount]
        @wraps(func)
        def decorated(*args,**kwargs):
            all_args = kwargs.copy()
            for n,v in zip(names,args):
                all_args[n] = v
            for k,v in trigger.iteritems():
                if k in all_args and all_args[k] != v:
                    break
            else:
                return target(all_args)
            return func(*args,**kwargs)
        decorated._names = names
        return decorated
    return decorator

Beispiel:

def interceptor1(kwargs):
    print 'Intercepted by #1!'

def interceptor2(kwargs):
    print 'Intercepted by #2!'

def interceptor3(kwargs):
    print 'Intercepted by #3!'

@intercept(interceptor1,arg1=20,arg2=5) # if arg1 == 20 and arg2 == 5
@intercept(interceptor2,arg1=20)        # elif arg1 == 20
@intercept(interceptor3,arg2=5)         # elif arg2 == 5
def foo(arg1,arg2):
    return arg1+arg2

>>> foo(3,4)
7
>>> foo(20,4)
Intercepted by #2!
>>> foo(3,5)
Intercepted by #3!
>>> foo(20,5)
Intercepted by #1!
>>>

functools.wraps tut, was der „einfache Dekorateur“ auf dem Wiki tut; Updates __doc__, __name__ und andere Attribute des Dekorateur.

Andere Tipps

Beachten Sie, dass

@foo
def bar():
    pass

entspricht:

def bar():
    pass
bar = foo(bar)

Wenn Sie also tun:

@foo(x=3)
def bar():
    pass

Das ist äquivalent zu:

def bar():
    pass
bar = foo(x=3)(bar)

so Ihr Dekorateur muss so etwas wie folgt aussehen:

def foo(x=1):
    def wrap(f):
        def f_foo(*args, **kw):
            # do something to f
            return f(*args, **kw)
        return f_foo
    return wrap

Mit anderen Worten, def wrap(f) ist wirklich der Dekorateur und foo(x=3) ist ein Funktionsaufruf, der einen Dekorateur zurück.

Sie können dies tun, indem Sie * args und ** kwargs im Dekorateur:

def intercept(func, *dargs, **dkwargs):
    def intercepting_func(*args, **kwargs):
        if (<some condition on dargs, dkwargs, args and kwargs>):
            print 'I intercepted you.'
        return func(*args, **kwargs)
    return intercepting_func

Es ist an Ihnen, wie Sie in Argumente zu übergeben wollen, um das Verhalten des Dekorateur zu steuern.

Um dies so transparent wie möglich machen an den Endverbraucher, können Sie den „einfachen Dekorateur“ verwenden, um auf die Python Wiki oder Michele Simionato "Dekorateur Dekorateur"

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top