没有参数装饰器装饰器?

时间:2010-08-26 01:04:32

标签: python

我写了一个看起来像这个

的装饰器
def login_required_message(*args, **kwargs):
    kwargs.setdefault('message', "You must be logged in to do that.")
    return _user_passes_test_message(lambda u: u.is_authenticated(), *args, **kwargs)

但是当我尝试在最后没有()的情况下使用它时,它会失败,除非我像这样重写它:

def login_required_message(function=None, *args, **kwargs):
    kwargs.setdefault('message', "You must be logged in to do that.")
    decorator = _user_passes_test_message(lambda u: u.is_authenticated(), *args, **kwargs)
    if function: return decorator(function)
    else: return decorator

然后()是可选的。那么,我如何将这个“可选”功能封装到装饰器中,以便我可以装饰我的装饰器以允许没有参数?

2 个答案:

答案 0 :(得分:3)

我实际上最近写了一篇关于此事的blog post - 至少,我认为它解决了你想要做的事情。对于后代,这是我想出的:

def opt_arguments(func):
    def meta_wrapper(*args, **kwargs):
        if len(args) == 1 and callable(args[0]):
            return func(args[0])
        else:
            def meta_func(inner_func):
                return func(inner_func, *args, **kwargs)
            return meta_func
    return meta_wrapper

顺便说一句,在试图弄清楚如何去做的过程中,我得出的结论是,这是几乎总是比必要更复杂的事情之一; - )

答案 1 :(得分:1)

这个答案的灵感来自 Python Cookbook 的 recipe 9.6

def mydecorator_with_opt_args(func=None, arg1="arg1-default", arg2="arg2-default"):
    if func is None:
        return partial(mydecorator_with_opt_args, arg1=arg1, arg2=arg2)
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(f"Do something with {arg1} and {arg2}")
        return func(*args, **kwargs)

    return wrapper

使用:

@mydecorator_with_opt_args(arg1="arg1-set", arg2="arg2-set")
def passer():
    pass
passer()

会输出

"Do something with arg1-set and arg2-set"

@mydecorator_with_opt_args
def passer2():
    pass
passer2()

会输出

"Do something with arg1-default and arg2-default"

要了解如果不指定参数(passer2)会发生什么: 放置装饰器与编写 passer2 = mydecorator_with_opt_args(passer2) 相同,这意味着忽略 if func is None 并且您使用默认参数应用装饰器

然而,如果你指定了 args (passer),放置一个装饰器和写 passer = mydecorator_with_opt_args(arg1="arg1-set", arg2="arg2-set")(passer) 是一样的

mydecorator_with_opt_args(arg1="arg1-set", arg2="arg2-set") 返回 partial(mydecorator_with_opt_args, arg1="arg1-set", arg2="arg2-set") 然后 partial(mydecorator_with_opt_args, arg1="arg1-set", arg2="arg2-set")(passer) 等价于 mydecorator_with_opt_args(func=passer, arg1="arg1-set", arg2="arg2-set")

请注意,您需要指定关键字参数。