首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >带有参数的Python3‘重复’装饰器:@重复(N)

带有参数的Python3‘重复’装饰器:@重复(N)
EN

Stack Overflow用户
提问于 2019-04-22 12:23:34
回答 1查看 2.6K关注 0票数 2

我已经看到(很好的)许多装潢师的教程和片段,包括我认为是规范的答案:Decorators with argumentspython decorator arguments with @ syntax,但我不明白为什么我的代码中会出现错误。

下面的代码位于文件decorators.py

代码语言:javascript
复制
#!/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交互控制台中测试了上面的内容:

代码语言:javascript
复制
>>> 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的https://stackoverflow.com/questions/12078288/scope-of-variables-in-python-decorators-changing-parameters标志的响应),根本不清楚在您所谓的复制中希望实现什么OP。我只能推测,他(她)打算从全局范围访问在装饰器包装器中定义的计数器,但没有将其声明为nonlocal。事实上,我们甚至不知道OP是处理Python 2还是处理3,尽管它在很大程度上与Python无关。我向您承认,错误消息非常相似,如果不是等效的,如果不是相同的话。然而,我的意图不是从全局范围访问内包装定义的计数器。我本打算把这个柜台变成纯本地的,于是我就做了。我的编码错误完全是在别处。实际上,Kevin (下面)提供的优秀讨论和解决方案是自然的,完全不同于仅仅在包装器定义块中添加一个nonlocal <var> (在Python3.x的情况下)。我不会重复凯文的论点。它们是清澈的,人人都能买到。

最后,我会说,错误消息可能是这里最不重要的信息,尽管这显然是我的错误代码的结果。我对此作出了补偿,但这篇文章绝对不是对“重复”的建议的翻版。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-04-22 12:43:24

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

代码语言:javascript
复制
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")

结果:

代码语言:javascript
复制
displaying: foo
displaying: bar

"foo“和"bar”各显示一次,而"baz“则被显示零次。我想这不是我们想要的行为。

由于display循环中的return func(*args, **kwargs),对while的前两个调用无法重复。返回语句导致wrapper_repeat立即终止,并且不会发生while的进一步迭代。因此,没有任何装饰功能会重复不止一次。一个可能的解决方案是删除return并调用该函数。

代码语言:javascript
复制
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")

结果:

代码语言:javascript
复制
displaying: foo
displaying: foo

"foo“显示了两次,但现在"bar”和"baz“都没有出现。这是因为nbrTimes在装饰器的所有实例中都是共享的,这要感谢nonlocal。一旦display("foo")nbrTimes降为零,即使在调用完成之后,它仍然保持在零。display("bar")display("baz")将执行它们的装饰器,查看nbrTimes为零,并在根本不调用修饰函数的情况下终止。

因此,您不希望您的循环计数器是非本地的。但这意味着您不能为此目的使用nbrTimes。尝试根据nbrTimes的值创建一个局部变量,并将其减少。

代码语言:javascript
复制
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")

结果:

代码语言:javascript
复制
displaying: foo
displaying: foo
displaying: bar
displaying: bar
displaying: baz
displaying: baz

..。同时,您也可以使用for循环而不是while

代码语言:javascript
复制
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")
票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55794369

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档