![](/img/trans.png)
[英]Python: can I use the lambda argument n to repeat lambda operation (n+n) n times?
[英]Python3 'repeat' decorator with argument: @repeat(n)
我已经看过(很棒的)许多带w和w / o参数的装饰器的教程和片段,包括我认为可以视为规范的答案的两个: 带参数的 装饰器,带@语法的python装饰器参数 ,但我不明白为什么我的代码中出现错误。
以下代码位于文件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
),因此执行时出现的“未使用的参数”警告和错误使我nbrTimes
。 对于Python来说还是相对较新的。 帮助非常感谢。
编辑:( 响应@recnac的重复标志)根本不清楚您声称的重复项中要实现的OP。 我只能推测他/她打算从全局范围访问装饰器包装器内定义的计数器,但未能将其声明为nonlocal
。 事实是,我们甚至都不知道OP处理的是Python 2还是3,尽管在这里与OP无关。 我向您承认错误消息非常相似,如果不相同,甚至不相同。 但是,我的目的不是从全局范围访问包装器内定义的计数器。 我打算使这个计数器纯粹是本地化的,并且做到了。 我的编码错误在其他地方。 事实证明,凯文(下面)提供的出色的讨论和解决方案是自然的,与在包装器定义块内添加nonlocal <var>
完全不同(对于Python 3.x)。 我不会重复凯文的论点。 它们清澈透明,可供所有人使用。
最终,我走了出去,会说错误消息可能是这里所有问题中最不重要的,即使这显然是我的错误代码造成的。 为此,我做出了修改,但此职位绝对不是对提议的“重复”内容的重新讨论。
提出的重复问题“ python装饰器中的变量范围-更改参数”提供了有用的信息,这些信息解释了为什么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”则显示0次。 我认为这不是理想的行为。
由于while
循环内有return func(*args, **kwargs)
因此无法重复display
的前两个调用。 return语句使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
。 尝试根据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")
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.