代码之家  ›  专栏  ›  技术社区  ›  Cbhihe

带参数的Python3装饰器

  •  0
  • Cbhihe  · 技术社区  · 6 年前

    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.
    

    我只是不知道我在哪里搞砸了。在其他示例中,传递的参数 NBR次 )不是 直到后来在内部函数,所以“未使用的参数”警告和执行时的错误留给我一种高高在上。对Python还是比较陌生的。非常感谢你的帮助。

    (针对 duplicate @recnac标记) 现在还不清楚你所谓的复制品想要达到什么样的效果。我只能猜测他/她打算从全局范围访问decorator包装器中定义的计数器,但没有将其声明为 nonlocal . 事实上,我们甚至不知道OP处理的是python2还是python3,尽管这里基本上不相关。我承认错误信息是非常相似的,如果不是等价的,如果不是相同的。但是,我的目的不是从全局范围访问包装器中定义的计数器。我打算让这个柜台完全是本地的,我做到了。我的编码错误完全在别处。事实证明,Kevin(下面)提供的优秀讨论和解决方案与添加 nonlocal <var>

    最后,我冒着风险说,错误消息在这里可能是最不重要的,尽管它显然是我糟糕代码的结果。对此我深表歉意,但这篇文章绝对不是拟议中的“复制品”的翻版。

    0 回复  |  直到 6 年前
        1
  •  3
  •   Kevin    6 年前

    提出的重复问题, Scope of variables in python decorators - changing parameters wrapper_repeat 考虑 nbrTimes 作为一个局部变量,如何 nonlocal 可能是用来让它识别 定义人 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 func(*args, **kwargs) 在你的 while 包装器\u重复 立即终止,并且 将会发生。因此,任何修饰函数都不会重复一次以上。一种可能的解决办法是移除 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
    

    在您的decorator的所有实例中共享,这要感谢 非局部的 . 一旦 display("foo") NBR次 如果设置为零,则即使在调用完成后,它仍保持为零。 display("bar") display("baz") 会处决他们的装饰师,明白吗 NBR次 为零,并且在根本不调用修饰函数的情况下终止。

    所以事实证明你没有 希望 循环计数器必须是非局部的。但这意味着你不能使用 NBR次 为此目的。尝试创建基于 NBR次

    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 循环而不是 虽然 .

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