1 回答

TA贡献1993条经验 获得超5个赞
提出的重复问题,python 装饰器中的变量范围 - 更改参数提供了有用的信息,解释了为什么wrapper_repeat认为nbrTimes是局部变量,以及如何nonlocal使用它来识别nbrTimes由repeat. 这将解决异常,但我认为这不是您的情况的完整解决方案。您的装饰功能仍然不会重复。
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 语句导致立即终止,并且不会发生进一步的迭代。所以没有装饰功能会重复一次以上。一种可能的解决方案是删除并调用该函数。return func(*args, **kwargs)whilewrapper_repeatwhilereturn
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")
添加回答
举报