我已经看到(很好的)许多装潢师的教程和片段,包括我认为是规范的答案:Decorators with arguments,python decorator arguments with @ syntax,但我不明白为什么我的代码中会出现错误。
下面的代码位于文件decorators.py中
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Description: decorators
"""
import functools
def repeat(nbrTimes=2):
'''
Define parametrized decorator with arguments
Default nbr of repeats is 2
'''
def real_repeat(func):
"""
Repeats execution 'nbrTimes' times
"""
@functools.wraps(func)
def wrapper_repeat(*args, **kwargs):
while nbrTimes != 0:
nbrTimes -= 1
return func(*args, **kwargs)
return wrapper_repeat
return real_repeat我从语法检查器得到的第一个警告是,nbrTimes是一个“未使用的参数”。
我在python3交互控制台中测试了上面的内容:
>>> from decorators import repeat
>>> @repeat(nbrTimes=3)
>>> def greetings():
>>> print("Howdy")
>>>
>>> greetings()
Traceback (most recent call last):
File "<stdin>", line 1 in <module>
File path/to/decorators.py, line xx in wrapper_repeat
'''
UnboundLocalError: local variable 'nbrTimes' referenced before assignment.我只是不知道我在哪里搞砸了。在其他示例中,传递的参数(此处为nbrTimes)直到内部函数的后面才被“使用”,因此执行时的“未使用的参数”警告和错误使我感到非常枯燥。对于Python来说还是比较新的。非常感激的帮助。
编辑:(作为对@recnac的https://stackoverflow.com/questions/12078288/scope-of-variables-in-python-decorators-changing-parameters标志的响应),根本不清楚在您所谓的复制中希望实现什么OP。我只能推测,他(她)打算从全局范围访问在装饰器包装器中定义的计数器,但没有将其声明为nonlocal。事实上,我们甚至不知道OP是处理Python 2还是处理3,尽管它在很大程度上与Python无关。我向您承认,错误消息非常相似,如果不是等效的,如果不是相同的话。然而,我的意图不是从全局范围访问内包装定义的计数器。我本打算把这个柜台变成纯本地的,于是我就做了。我的编码错误完全是在别处。实际上,Kevin (下面)提供的优秀讨论和解决方案是自然的,完全不同于仅仅在包装器定义块中添加一个nonlocal <var> (在Python3.x的情况下)。我不会重复凯文的论点。它们是清澈的,人人都能买到。
最后,我会说,错误消息可能是这里最不重要的信息,尽管这显然是我的错误代码的结果。我对此作出了补偿,但这篇文章绝对不是对“重复”的建议的翻版。
发布于 2019-04-22 12:43:24
提出的重复问题,Scope of variables in python decorators - changing parameters提供了有用的信息,解释了为什么wrapper_repeat将nbrTimes视为局部变量,以及如何使用nonlocal来使其识别repeat定义的nbrTimes。这将修复异常,但在您的情况下,我不认为它是一个完整的解决方案。您的装饰功能仍然不会重复。
import functools
def repeat(nbrTimes=2):
'''
Define parametrized decorator with arguments
Default nbr of repeats is 2
'''
def real_repeat(func):
"""
Repeats execution 'nbrTimes' times
"""
@functools.wraps(func)
def wrapper_repeat(*args, **kwargs):
nonlocal nbrTimes
while nbrTimes != 0:
nbrTimes -= 1
return func(*args, **kwargs)
return wrapper_repeat
return real_repeat
@repeat(2)
def display(x):
print("displaying:", x)
display("foo")
display("bar")
display("baz")结果:
displaying: foo
displaying: bar"foo“和"bar”各显示一次,而"baz“则被显示零次。我想这不是我们想要的行为。
由于display循环中的return func(*args, **kwargs),对while的前两个调用无法重复。返回语句导致wrapper_repeat立即终止,并且不会发生while的进一步迭代。因此,没有任何装饰功能会重复不止一次。一个可能的解决方案是删除return并调用该函数。
import functools
def repeat(nbrTimes=2):
'''
Define parametrized decorator with arguments
Default nbr of repeats is 2
'''
def real_repeat(func):
"""
Repeats execution 'nbrTimes' times
"""
@functools.wraps(func)
def wrapper_repeat(*args, **kwargs):
nonlocal nbrTimes
while nbrTimes != 0:
nbrTimes -= 1
func(*args, **kwargs)
return wrapper_repeat
return real_repeat
@repeat(2)
def display(x):
print("displaying:", x)
display("foo")
display("bar")
display("baz")结果:
displaying: foo
displaying: foo"foo“显示了两次,但现在"bar”和"baz“都没有出现。这是因为nbrTimes在装饰器的所有实例中都是共享的,这要感谢nonlocal。一旦display("foo")将nbrTimes降为零,即使在调用完成之后,它仍然保持在零。display("bar")和display("baz")将执行它们的装饰器,查看nbrTimes为零,并在根本不调用修饰函数的情况下终止。
因此,您不希望您的循环计数器是非本地的。但这意味着您不能为此目的使用nbrTimes。尝试根据nbrTimes的值创建一个局部变量,并将其减少。
import functools
def repeat(nbrTimes=2):
'''
Define parametrized decorator with arguments
Default nbr of repeats is 2
'''
def real_repeat(func):
"""
Repeats execution 'nbrTimes' times
"""
@functools.wraps(func)
def wrapper_repeat(*args, **kwargs):
times = nbrTimes
while times != 0:
times -= 1
func(*args, **kwargs)
return wrapper_repeat
return real_repeat
@repeat(2)
def display(x):
print("displaying:", x)
display("foo")
display("bar")
display("baz")结果:
displaying: foo
displaying: foo
displaying: bar
displaying: bar
displaying: baz
displaying: baz..。同时,您也可以使用for循环而不是while。
import functools
def repeat(nbrTimes=2):
'''
Define parametrized decorator with arguments
Default nbr of repeats is 2
'''
def real_repeat(func):
"""
Repeats execution 'nbrTimes' times
"""
@functools.wraps(func)
def wrapper_repeat(*args, **kwargs):
for _ in range(nbrTimes):
func(*args, **kwargs)
return wrapper_repeat
return real_repeat
@repeat(2)
def display(x):
print("displaying:", x)
display("foo")
display("bar")
display("baz")https://stackoverflow.com/questions/55794369
复制相似问题