gpt4 book ai didi

带有参数 : @repeat(n) 的 Python3 'repeat' 装饰器

转载 作者:行者123 更新时间:2023-12-02 20:01:20 31 4
gpt4 key购买 nike

我已经看到(很多)许多教程和装饰器的片段 w/和 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是一个“未使用的参数”。

我在 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 标记的 duplicate)根本不清楚您声称的副本中的 OP 想要实现什么。我只能推测他/她打算从全局范围访问装饰器包装器内定义的计数器,但未能将其声明为 nonlocal。 .事实上,我们甚至不知道 OP 是处理 Python 2 还是 3,尽管这在很大程度上无关紧要。我承认错误消息非常相似,如果不等同,如果不相同。但是,我的意图不是从全局范围访问包装内定义的计数器。我打算让这个柜台纯粹是本地的,并且做到了。我的编码错误完全在别处。事实证明,Kevin(下图)提供的出色讨论和解决方案的性质与仅添加 nonlocal <var> 完全不同。在包装器定义 block 内(在 Python 3.x 的情况下)。我不会重复凯文的论点。它们清澈透明,可供所有人使用。

最后我冒昧地说,错误消息可能是这里所有消息中最不重要的,尽管它显然是我的错误代码的结果。为此,我做出了修正,但这篇文章绝对不是对提议的“重复”的重复。

最佳答案

提出的重复问题,Scope of variables in python decorators - changing parameters提供有用的信息,解释为什么 wrapper_repeatnbrTimes 视为局部变量,以及如何使用 nonlocal 使其识别 nbrTimesrepeat 定义。这将修复异常,但我认为这不是您的情况的完整解决方案。您的装饰函数仍然不会重复。

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”显示零次。我认为这不是所需的行为。

由于 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")

关于带有参数 : @repeat(n) 的 Python3 'repeat' 装饰器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55794369/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com