为了账号安全,请及时绑定邮箱和手机立即绑定

Python3 带有参数的“重复”装饰器:@repeat(n)

Python3 带有参数的“重复”装饰器:@repeat(n)

蝴蝶刀刀 2022-01-18 16:38:37
我已经看到(很多)教程和带参数和不带参数的装饰器片段,包括那些我认为是规范答案的两个:带参数的装饰器,带@语法的python装饰器参数,但我不明白为什么我的代码中出现错误。下面的代码位于文件中decorators.py:#!/usr/bin/env python3# -*- coding: utf-8 -*-"""Description: decorators"""import functoolsdef 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 的情况下)。我不会重复凯文的论点。它们是清晰的,可供所有人使用。最后,我冒昧地说,错误消息可能是这里最不重要的,即使它显然是我的错误代码的结果。为此我进行了修正,但这篇文章绝对不是对提议的“重复”的重新讨论。
查看完整描述

1 回答

?
ibeautiful

TA贡献1993条经验 获得超5个赞

提出的重复问题,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)whilewrapper_repeatwhilereturn


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


查看完整回答
反对 回复 2022-01-18

添加回答

代码语言

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号