【问题标题】:Calling Decorator without Parentheses (without changing decorator definition) [duplicate]不带括号调用装饰器(不更改装饰器定义)[重复]
【发布时间】:2020-02-26 18:33:24
【问题描述】:

假设我有以下装饰器。 (重复一个函数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: 在尝试那里给出的双包装定义之前,我没有问这个问题。不带括号调用它会返回另一个函数(如果函数的签名如所述)。

【问题讨论】:

  • 这是一个略有不同的问题,但接受的答案确实回答了这个问题。
  • 其中一个答案确实显示了一个装饰器被用作@foo(不仅仅是@foo())和@foo(...)
  • 该问题中的定义具有不同的签名。在当前,def repeat 语句没有函数参数。该问题中的函数有 2 级嵌套,而我的有 3 级嵌套。我想问是否可以使用这样的装饰器定义(用括号调用)而不更改函数定义。那里接受的答案有不同的签名,因此在这个问题中不需要肉。
  • 请看我修改后的答案

标签: python decorator python-decorators parentheses


【解决方案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

【讨论】:

  • 我们不需要@enable_direct@repeat(4) 没有它也可以工作。还需要工作的是不带括号的@repeat
  • 请看我修改后的答案@Hrishikesh
  • 太棒了。谢谢!
猜你喜欢
  • 2017-10-17
  • 2016-06-05
  • 2020-05-19
  • 2017-04-03
  • 2013-02-06
  • 2011-10-04
  • 2022-08-15
  • 2022-08-15
相关资源
最近更新 更多