简体   繁体   English

如何在装饰器中使用名为 arguments 的名称?

[英]How can I use named arguments in a decorator?

If I have the following function:如果我有以下 function:


def intercept(func):
  # do something here

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

I would like for intercept to fire up only when arg1 is 20. I would like to be able to pass named parameters to the function.我希望仅在arg1为 20 时触发拦截。我希望能够将命名参数传递给 function。 How could I accomplish this?我怎么能做到这一点?

Here's a little code sample:这是一个小代码示例:



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")

This throws the following exception TypeError: intercept() got an unexpected keyword argument 'a'这会引发以下异常 TypeError: intercept() got an unexpected keyword argument 'a'

Remember that请记住

@foo
def bar():
    pass

is equivalent to:相当于:

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

so if you do:所以如果你这样做:

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

that's equivalent to:这相当于:

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

so your decorator needs to look something like this:所以你的装饰器需要看起来像这样:

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

In other words, def wrap(f) is really the decorator, and foo(x=3) is a function call that returns a decorator.换句话说, def wrap(f)是真正的装饰器,而foo(x=3)是一个返回装饰器的 function 调用。

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

Example:例子:

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 does what the "simple decorator" on the wiki does; functools.wraps做了 wiki 上的“简单装饰器”所做的事情; Updates __doc__ , __name__ and other attribute of the decorator.更新__doc____name__和装饰器的其他属性。

You can do this by using *args and **kwargs in the decorator:您可以通过在装饰器中使用 *args 和 **kwargs 来做到这一点:

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

It's up to you how you wish to pass in arguments to control the behavior of the decorator.由您决定如何传入 arguments 来控制装饰器的行为。

To make this as transparent as possible to the end user, you can use the "simple decorator" on the Python wiki or Michele Simionato's "decorator decorator"为了使最终用户尽可能透明,您可以使用Python wiki上的“简单装饰器”或 Michele Simionato 的“装饰器装饰器”

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM