我刚刚开始学习装饰器。我觉得我理解如何修改装饰器以使用参数,但我不明白为什么Python中的装饰器已经实现,因此需要进行此修改。
[编辑:为清楚起见。] 对于没有参数的装饰器,以下是等效的:
@decorate
def function(arg):
pass
function = decorate(function)
那么,对于带参数的装饰器,为什么不让下面的内容相同?
@decorate(123)
def function(arg):
pass
function = decorate(function, 123)
我发现这更一致,更易于阅读,也更容易编码。
下面是python装饰器如何工作,然后是我期望它们如何工作。 [编辑:我也添加了预期的输出。]:
OUTPUT:
function HELLA NESTED! Decorated with OMG!
function NOT AS NESTED! Decorated with NOT SO BAD!
def python_decorator(dec_arg):
def actual_decorator(func):
def decorated_func(func_arg):
print(func.__name__, func(func_arg), "Decorated with", dec_arg)
return decorated_func
return actual_decorator
@python_decorator("OMG!")
def function(arg):
return arg
function("HELLA NESTED!")
def my_expected_decorator(func, dec_arg):
# I expected the `func` parameter to be like `self` in classes---it's
# always first and is given special meaning with decorators.
def decorated_func(func_arg):
print(func.__name__, func(func_arg), "Decorated with", dec_arg)
return decorated_func
# @my_expected_decorator("NOT SO BAD!") # This is what I expected.
def function(arg):
return arg
function = my_expected_decorator(function, "NOT SO BAD!")
function("NOT AS NESTED!")
我看了PEP 318并且没有看到装饰器没有以上述方式实现的原因,但我再次对这个想法不熟悉。我觉得上面的改变会使装饰器更容易使用,也更加一致。
答案 0 :(得分:4)
选择当前实施的原因是因为它更多一致。如果以你建议的方式实现它,python必须根据它们的使用方式以非常不同的方式处理装饰器:
@python_decorator
之类的其他参数,则可以使用函数作为唯一参数立即调用它。@python_decorator("OMG!")
之类的参数调用装饰器,python必须在某处存储这些参数,然后将该函数作为第一个参数插入。@python_decorator()
这样的参数,会发生什么?这应该等同于@python_decorator
吗?但是为什么有两种不同的方法来使用没有参数的装饰器?也许不应该允许这种语法?正如您所看到的,这样的实现会增加一些不一致性。另一方面,所选择的实现非常直观:@
符号后面的代码可以被认为是一个表达式,该表达式的结果用作装饰器:
@python_decorator
的情况下,python_decorator
是一个只返回现有装饰器函数的表达式。@python_decorator("OMG!")
,表达式python_decorator("OMG!")
也会返回装饰器函数。从这个意义上说,所选择的实现比你提出的实现更加一致。
此基本原理也在the PEP中解释:
具有返回装饰器的函数的基本原理是 @符号后面的部分可以被认为是一个表达式 (虽然在语法上仅限于一个函数),等等 调用表达式返回。请参阅declaration arguments [16]。
关于评论中提出的建议:
禁用语法@python_decorator
语法并强制执行@python_decorator()
我看到的问题是,圆括号没有任何实际意义,只会增加混乱。绝大多数装饰器都是在没有参数的情况下应用的。更改此语法只是为了与带参数的装饰器保持一致是不合理的。
将装饰函数显式传递为第一个参数
同样,这不会增加任何语义含义。很明显,装饰器将被调用在@python_decorator
行正下方定义的函数上。强制用户显式传递函数作为参数是坏的,因为
func = decorator(func)
语法。必须写@decorator(func)
而不是真正的重大改进,不是吗?