繁体   English   中英

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

[英]Python3 'repeat' decorator with argument: @repeat(n)

我已经看过(很棒的)许多带w和w / o参数的装饰器的教程和片段,包括我认为可以视为规范的答案的两个: 带参数的 装饰器,带@语法的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 ),因此执行时出现的“未使用的参数”警告和错误使我nbrTimes 对于Python来说还是相对较新的。 帮助非常感谢。

编辑:( 响应@recnac的重复标志)根本不清楚您声称的重复项中要实现的OP。 我只能推测他/她打算从全局范围访问装饰器包装器内定义的计数器,但未能将其声明为nonlocal 事实是,我们甚至都不知道OP处理的是Python 2还是3,尽管在这里与OP无关。 我向您承认错误消息非常相似,如果不相同,甚至不相同。 但是,我的目的不是从全局范围访问包装器内定义的计数器。 我打算使这个计数器纯粹是本地化的,并且做到了。 我的编码错误在其他地方。 事实证明,凯文(下面)提供的出色的讨论和解决方案是自然的,与在包装器定义块内添加nonlocal <var>完全不同(对于Python 3.x)。 我不会重复凯文的论点。 它们清澈透明,可供所有人使用。

最终,我走了出去,会说错误消息可能是这里所有问题中最不重要的,即使这显然是我的错误代码造成的。 为此,我做出了修改,但此职位绝对不是对提议的“重复”内容的重新讨论。

提出的重复问题“ python装饰器中的变量范围-更改参数”提供了有用的信息,这些信息解释了为什么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”分别仅显示一次,而“ 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 尝试根据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")

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM