python - Python3 带有参数的“重复”装饰器:@repeat(n)
问题描述
我已经看到(很多)教程和带参数和不带参数的装饰器片段,包括那些我认为是规范答案的两个:带参数的装饰器,带@语法的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
)直到稍后在内部函数中才“使用” ,因此“未使用的参数”警告和执行时的错误让我有点兴奋和干燥。对 Python 来说还是比较新的。非常感谢帮助。
编辑:(作为对重复的回应@recnac标志)
根本不清楚您声称的副本中的 OP 想要实现什么。我只能推测他/她打算从全局范围访问装饰器包装器内定义的计数器,但未能将其声明为nonlocal
。事实上,我们甚至不知道 OP 是处理 Python 2 还是 Python 3,尽管它在这里基本上无关紧要。我向你承认,错误消息非常相似,如果不相等,如果不是一样。但是我的意图不是从全局范围访问包装器内定义的计数器。我打算使这个计数器纯粹是本地的,并且确实做到了。我的编码错误完全在其他地方。事实证明,由提供的出色讨论和解决方案Kevin(下图)是一种性质,与仅仅在包装器定义块中添加一个完全不同nonlocal <var>
(在 Python 3.x 的情况下)。我不会重复 Kevin 的论点。它们是清晰的并且可供所有人使用。
最后,我冒昧地说,错误消息可能是这里最不重要的,即使它显然是我的错误代码的结果。为此我进行了修正,但这篇文章绝对不是对提议的“重复”的重新讨论。
解决方案
提出的重复问题,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)
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")
推荐阅读
- javascript - 如何在使用 Javascript 初始加载后检测网页是否正在重新加载?
- angular - Angular 6 中两个具有通用组件的延迟加载模块
- asp.net - Auth0 查询参数 (q=) 未返回预期结果
- c++ - 内置类型的基于 C++ 类型的分派
- c# - NetworkCredential 在 IIS 服务器第二个请求之后不起作用
- .net-core - 运行 dotnet 核心应用程序时找不到本地主机
- android - 无法绑定/连接到作为 AAR 库的一部分创建的远程服务
- android - 如何以编程方式为android启用前置摄像头而不是后置摄像头?
- assembly - 当我们有编译器时,为什么我们甚至需要汇编器?
- ios - 在 WKWebView 中实现 Facebook 评论插件