在Python中,使用相同的装饰器带有和不带括号有什么区别?
例如:
不带括号:
例如:
不带括号:
@some_decorator
def some_method():
pass
带括号:
@some_decorator()
def some_method():
pass
some_decorator
在第一个代码片段中是一个常规装饰器:
@some_decorator
def some_method():
pass
相当于
some_method = some_decorator(some_method)
另一方面,第二个代码片段中的some_decorator
是一个可调用对象,它返回一个装饰器:
@some_decorator()
def some_method():
pass
等同于
some_method = some_decorator()(some_method)
正如 Duncan 在评论中指出的那样,一些装饰器设计成可以双向工作。下面是这样一个装饰器的基本实现:
def some_decorator(arg=None):
def decorator(func):
def wrapper(*a, **ka):
return func(*a, **ka)
return wrapper
if callable(arg):
return decorator(arg) # return 'wrapper'
else:
return decorator # ... or 'decorator'
pytest.fixture
是一个更复杂的例子。
@pytest.fixture
可以直接用作装饰器,或者带有一些命名参数调用它,这样它就会返回一个装饰器。 - DuncansomeDecorator()
总是返回相同的结果,因为它不接受任何参数(除非 someDecorator
保存了状态)。因此,我认为可调用装饰器保存了状态? - Utku@some_decorator
和@some_decorator()
之间的区别的关键是前者是装饰器,而后者是返回装饰器的函数(或可调用对象)。我认为看到每种情况的实现有助于理解它们之间的区别:@some_decorator
def some_decorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
应用:
@some_decorator
def some_method():
pass
等价性:
some_method = some_decorator(some_method)
@some_decorator()
def some_decorator():
def decorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
return decorator
应用:
@some_decorator()
def some_method():
pass
等价性:some_method = some_decorator()(some_method)
注意现在更容易看出@some_decorator()
是一个返回装饰器的函数,而some_decorator
只是一个装饰器。请记住,有些装饰器可以双向使用。
现在你可能想知道为什么我们有这两种情况,当前一版本似乎更简单。答案是如果你想要将参数传递给装饰器,使用@some_decorator()
将允许你这样做。让我们看一些代码示例:
def some_decorator(arg1, arg2):
def decorator(func):
def wrapper(*args, **kwargs):
print(arg1)
print(arg2)
return func(*args, **kwargs)
return wrapper
return decorator
应用:
@some_decorator('hello', 'bye')
def some_method():
pass
等价性:
some_method = some_decorator('hello', 'bye')(some_method)
注意:我认为值得一提的是,装饰器可以作为函数或类来实现。有关更多信息,请查看此处。
>>> @omittable_parentheses(allow_partial=True)
... def multiplier(multiply_by=2):
... def decorator(func):
... def multiplying_wrapper(*args, **kwargs):
... return multiply_by * func(*args, **kwargs)
... return multiplying_wrapper
... return decorator
...
>>> @multiplier
... def no_parentheses():
... return 2
...
>>> no_parentheses()
4
>>> @multiplier()
... def parentheses():
... return 2
...
>>> parentheses()
4
>>> @multiplier(3)
... def parameter():
... return 2
...
>>> parameter()
6
allow_partial=True
,则生成的装饰器也将与 partial
一起使用。>>> from functools import partial
>>> multiply_by_3 = partial(multiplier, multiply_by=3)
>>>
>>> @multiply_by_3
... def partial_no_parentheses():
... return 2
...
>>> partial_no_parentheses()
6
>>> @multiply_by_3()
... def partial_parentheses():
... return 2
...
>>> partial_parentheses()
6
装饰器代码:
from functools import wraps
def omittable_parentheses(maybe_decorator=None, /, allow_partial=False):
"""A decorator for decorators that allows them to be used without parentheses"""
def decorator(func):
@wraps(decorator)
def wrapper(*args, **kwargs):
if len(args) == 1 and callable(args[0]):
if allow_partial:
return func(**kwargs)(args[0])
elif not kwargs:
return func()(args[0])
return func(*args, **kwargs)
return wrapper
if maybe_decorator is None:
return decorator
else:
return decorator(maybe_decorator)
一些实际工作的代码,其中您在装饰器内使用参数:
def someDecorator(arg=None):
def decorator(func):
def wrapper(*a, **ka):
if not callable(arg):
print (arg)
return func(*a, **ka)
else:
return 'xxxxx'
return wrapper
if callable(arg):
return decorator(arg) # return 'wrapper'
else:
return decorator # ... or 'decorator'
@someDecorator(arg=1)
def my_func():
print('aaa')
@someDecorator
def my_func1():
print('bbb')
if __name__ == "__main__":
my_func()
my_func1()
输出结果为:
1
aaa
foo
和foo()
之间的差别是一样的,无论在什么地方,甚至不在装饰器中。 - Mike Graham