簡體   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