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 的情况下)。我不会重复凯文的论点。它们是清晰的,可供所有人使用。


最后,我冒昧地说,错误消息可能是这里最不重要的,即使它显然是我的错误代码的结果。为此我进行了修正,但这篇文章绝对不是对提议的“重复”的重新讨论。


蝴蝶刀刀
浏览 534回答 1
1回答

ibeautiful

提出的重复问题,python 装饰器中的变量范围 - 更改参数提供了有用的信息,解释了为什么wrapper_repeat认为nbrTimes是局部变量,以及如何nonlocal使用它来识别nbrTimes由repeat. 这将解决异常,但我认为这不是您的情况的完整解决方案。您的装饰功能仍然不会重复。import functoolsdef repeat(nbrTimes=2):&nbsp; &nbsp; '''&nbsp; &nbsp; Define parametrized decorator with arguments&nbsp; &nbsp; Default nbr of repeats is 2&nbsp; &nbsp; '''&nbsp; &nbsp; def real_repeat(func):&nbsp; &nbsp; &nbsp; &nbsp; """&nbsp; &nbsp; &nbsp; &nbsp; Repeats execution 'nbrTimes' times&nbsp; &nbsp; &nbsp; &nbsp; """&nbsp; &nbsp; &nbsp; &nbsp; @functools.wraps(func)&nbsp; &nbsp; &nbsp; &nbsp; def wrapper_repeat(*args, **kwargs):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; nonlocal nbrTimes&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; while nbrTimes != 0:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; nbrTimes -= 1&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return func(*args, **kwargs)&nbsp; &nbsp; &nbsp; &nbsp; return wrapper_repeat&nbsp; &nbsp; return real_repeat@repeat(2)def display(x):&nbsp; &nbsp; print("displaying:", x)display("foo")display("bar")display("baz")结果:displaying: foodisplaying: bar"foo" 和 "bar" 分别只显示一次,而 "baz" 显示零次。我认为这不是理想的行为。由于循环内部,前两个调用display无法重复。return 语句导致立即终止,并且不会发生进一步的迭代。所以没有装饰功能会重复一次以上。一种可能的解决方案是删除并调用该函数。return func(*args, **kwargs)whilewrapper_repeatwhilereturnimport functoolsdef repeat(nbrTimes=2):&nbsp; &nbsp; '''&nbsp; &nbsp; Define parametrized decorator with arguments&nbsp; &nbsp; Default nbr of repeats is 2&nbsp; &nbsp; '''&nbsp; &nbsp; def real_repeat(func):&nbsp; &nbsp; &nbsp; &nbsp; """&nbsp; &nbsp; &nbsp; &nbsp; Repeats execution 'nbrTimes' times&nbsp; &nbsp; &nbsp; &nbsp; """&nbsp; &nbsp; &nbsp; &nbsp; @functools.wraps(func)&nbsp; &nbsp; &nbsp; &nbsp; def wrapper_repeat(*args, **kwargs):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; nonlocal nbrTimes&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; while nbrTimes != 0:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; nbrTimes -= 1&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; func(*args, **kwargs)&nbsp; &nbsp; &nbsp; &nbsp; return wrapper_repeat&nbsp; &nbsp; return real_repeat@repeat(2)def display(x):&nbsp; &nbsp; print("displaying:", x)display("foo")display("bar")display("baz")结果:displaying: foodisplaying: foo“foo”被显示了两次,但现在“bar”和“baz”都没有出现。这是因为nbrTimes在装饰器的所有实例之间共享,这要归功于nonlocal. 一旦display("foo")递减nbrTimes到零,即使在调用完成后它也保持为零。display("bar")并将display("baz")执行他们的装饰器,看到它nbrTimes是零,并终止而不调用装饰函数。所以事实证明你不希望你的循环计数器是非本地的。但这意味着您不能nbrTimes用于此目的。尝试根据nbrTimes' 值创建一个局部变量,然后将其递减。import functoolsdef repeat(nbrTimes=2):&nbsp; &nbsp; '''&nbsp; &nbsp; Define parametrized decorator with arguments&nbsp; &nbsp; Default nbr of repeats is 2&nbsp; &nbsp; '''&nbsp; &nbsp; def real_repeat(func):&nbsp; &nbsp; &nbsp; &nbsp; """&nbsp; &nbsp; &nbsp; &nbsp; Repeats execution 'nbrTimes' times&nbsp; &nbsp; &nbsp; &nbsp; """&nbsp; &nbsp; &nbsp; &nbsp; @functools.wraps(func)&nbsp; &nbsp; &nbsp; &nbsp; def wrapper_repeat(*args, **kwargs):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; times = nbrTimes&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; while times != 0:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; times -= 1&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; func(*args, **kwargs)&nbsp; &nbsp; &nbsp; &nbsp; return wrapper_repeat&nbsp; &nbsp; return real_repeat@repeat(2)def display(x):&nbsp; &nbsp; print("displaying:", x)display("foo")display("bar")display("baz")结果:displaying: foodisplaying: foodisplaying: bardisplaying: bardisplaying: bazdisplaying: baz...当您使用它时,您也可以使用for循环而不是while.import functoolsdef repeat(nbrTimes=2):&nbsp; &nbsp; '''&nbsp; &nbsp; Define parametrized decorator with arguments&nbsp; &nbsp; Default nbr of repeats is 2&nbsp; &nbsp; '''&nbsp; &nbsp; def real_repeat(func):&nbsp; &nbsp; &nbsp; &nbsp; """&nbsp; &nbsp; &nbsp; &nbsp; Repeats execution 'nbrTimes' times&nbsp; &nbsp; &nbsp; &nbsp; """&nbsp; &nbsp; &nbsp; &nbsp; @functools.wraps(func)&nbsp; &nbsp; &nbsp; &nbsp; def wrapper_repeat(*args, **kwargs):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for _ in range(nbrTimes):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; func(*args, **kwargs)&nbsp; &nbsp; &nbsp; &nbsp; return wrapper_repeat&nbsp; &nbsp; return real_repeat@repeat(2)def display(x):&nbsp; &nbsp; print("displaying:", x)display("foo")display("bar")display("baz")
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Python