Python3 'repeat' 带有参数的装饰器:@repeat(n)
Python3 'repeat' decorator with argument: @repeat(n)
我看过(很多)许多带有和 w/o 参数的装饰器教程和片段,包括我认为是规范答案的那两个: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
是一个 "unused argument"。
我在 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
)直到稍后在内部函数中才为 "used",因此 "unused argument" 警告和执行错误让我有点高和干燥。 Python 还是比较新的。非常感谢帮助。
编辑:(响应@recnac 的 duplicate 标志)
根本不清楚您声称的副本中的 OP 想要实现什么。我只能推测 he/she 打算从全局范围访问装饰器包装器内定义的计数器,但未能将其声明为 nonlocal
。事实上,我们甚至不知道 OP 处理的是 Python 2 还是 3,尽管这在很大程度上无关紧要。我承认错误消息非常相似,如果不等同,如果不相同。但是,我的意图不是从全局范围访问包装内定义的计数器。我打算让这个柜台纯粹是本地的,并且做到了。我的编码错误完全在别处。事实证明,Kevin(下文)提供的出色讨论和解决方案具有某种性质,与仅在包装器定义块中添加 nonlocal <var>
完全不同(在 Python 3.x 的情况下) .我不会重复凯文的论点。它们清澈透明,可供所有人使用。
最后我冒昧地说,错误消息可能是这里所有消息中最不重要的,尽管它显然是我的错误代码的结果。为此,我做出了补偿,但是这个 post 绝对不是对提议的 "duplicate".
的翻版
提议的重复问题,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"各只显示1次,"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
' 的值创建局部变量,然后将其递减。
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")
我看过(很多)许多带有和 w/o 参数的装饰器教程和片段,包括我认为是规范答案的那两个: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
是一个 "unused argument"。
我在 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
)直到稍后在内部函数中才为 "used",因此 "unused argument" 警告和执行错误让我有点高和干燥。 Python 还是比较新的。非常感谢帮助。
编辑:(响应@recnac 的 duplicate 标志)
根本不清楚您声称的副本中的 OP 想要实现什么。我只能推测 he/she 打算从全局范围访问装饰器包装器内定义的计数器,但未能将其声明为 nonlocal
。事实上,我们甚至不知道 OP 处理的是 Python 2 还是 3,尽管这在很大程度上无关紧要。我承认错误消息非常相似,如果不等同,如果不相同。但是,我的意图不是从全局范围访问包装内定义的计数器。我打算让这个柜台纯粹是本地的,并且做到了。我的编码错误完全在别处。事实证明,Kevin(下文)提供的出色讨论和解决方案具有某种性质,与仅在包装器定义块中添加 nonlocal <var>
完全不同(在 Python 3.x 的情况下) .我不会重复凯文的论点。它们清澈透明,可供所有人使用。
最后我冒昧地说,错误消息可能是这里所有消息中最不重要的,尽管它显然是我的错误代码的结果。为此,我做出了补偿,但是这个 post 绝对不是对提议的 "duplicate".
的翻版提议的重复问题,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"各只显示1次,"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
' 的值创建局部变量,然后将其递减。
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")