调用带括号的装饰器(不更改装饰器定义)

时间:2019-10-31 11:46:07

标签: python decorator python-decorators parentheses

假设我有以下装饰器。 (要重复一个功能n次)

def repeat(num_times=4):
    def decorator_repeat(func):
        @functools.wraps(func)
        def wrapper_repeat(*args, **kwargs):
            for _ in range(num_times):
                value = func(*args, **kwargs)
            return value
        return wrapper_repeat
    return decorator_repeat

现在,它的默认值确实为4,但是即使我想使用默认值调用它,我仍然必须按以下方式调用它

@repeat()
def my_function():
    print("hello")

代替

@repeat
def my_function():
    print("hello")

现在,我可以将装饰器的定义更改为

def repeat(_func=None, *, num_times=2):
    def decorator_repeat(func):
        @functools.wraps(func)
        def wrapper_repeat(*args, **kwargs):
            for _ in range(num_times):
                value = func(*args, **kwargs)
            return value
        return wrapper_repeat

    if _func is None:
        return decorator_repeat
    else:
        return decorator_repeat(_func)

如果需要,启用该功能以不带参数的方式调用它。

但是,这可以在不更改装饰器代码的情况下,而是通过定义另一个装饰器来实现吗?

即我想定义一个装饰器enable_direct,以便可以将@enable_direct添加到我的装饰器定义中,并具有相同的效果。 (即如下)

@enable_direct
def repeat(num_times=4):
    def decorator_repeat(func):
        @functools.wraps(func)
        def wrapper_repeat(*args, **kwargs):
            for _ in range(num_times):
                value = func(*args, **kwargs)
            return value
        return wrapper_repeat
    return decorator_repeat

注意:

我知道How to create a Python decorator that can be used either with or without parameters?

中提到的解决方案

该问题中的定义具有不同的签名,并且如果重新开始,则可以遵循该模式。但是,说我有20-30个这样的装饰器定义(嵌套3级)。我希望所有这些都可以不带括号地被调用。 def repeat语句没有函数参数。该问题中的功能具有2级嵌套,而我的具有3级嵌套。我想问一问这样的修饰符定义(用括号来调用)是否可能,而无需更改函数定义。那里被接受的答案具有不同的签名,因此在该问题中无需添加肉食。

注意2:在尝试这里给出的双重包装的定义之前,我没有问过这个问题。调用时不带括号将返回另一个函数(如果函数的签名如上所述)。

1 个答案:

答案 0 :(得分:1)

您在这里:

import functools


def enable_direct(decorator):
    @functools.wraps(decorator)
    def wrapper(*args, **kwargs):
        f = args[0]
        if callable(f):
            return decorator()(f)  # pass the function to be decorated
        else:
            return decorator(*args, **kwargs)  # pass the specified params
    return wrapper


@enable_direct
def repeat(num_times=4):
    def decorator_repeat(func):
        @functools.wraps(func)
        def wrapper_repeat(*args, **kwargs):
            for _ in range(num_times):
                value = func(*args, **kwargs)
            return value
        return wrapper_repeat
    return decorator_repeat


@repeat
def my_func(name):
    print(name)

@repeat(2)
def my_func2(name):
    print(name)


print(my_func)
print(my_func2)

my_func("Gino")
my_func2("Mario")

哪个生产

<function my_func at 0x7f629f091b70>
<function my_func2 at 0x7f629f091bf8>
Gino
Gino
Gino
Gino
Mario
Mario