簡體   English   中英

如何實現裝飾器功能

[英]How to implement a decorator function

我對裝飾器和閉合器是全新的,我嘗試通過一個簡單的示例進行練習。 執行時會引發以下錯誤:

NameError: name 'congratulate' is not defined

我需要更改什么?

"""
A recursive function to check if a string is a palindrome.
"""

@congratulate
def palindrome(phrase):
    characters = [char.lower() for char in phrase if char.isalpha()]
    chars_len = len(characters)

    out1 = characters[0]
    out2 = characters[-1]

    if chars_len <= 2:
        return out1 == out2
    else:
        if out1 == out2:
            return palindrome(characters[1:-1])
        else:
            return False


def congratulate(func):
    if func:
        print('Congratulations, it\'s a palindrome!')


if __name__ == '__main__':
    print(palindrome('Rats live on no evil star'))
"""
A recursive function to check if a string is a palindrome.
"""

def congratulate(func):
    def wrapper(*argv, **kargs):
        result = func(*argv, **kargs)
        if result:
            print('Congratulations, it\'s a palindrome!')
        return result

    return wrapper

@congratulate
def palindrome(phrase):
    characters = [char.lower() for char in phrase if char.isalpha()]
    chars_len = len(characters)

    out1 = characters[0]
    out2 = characters[-1]

    if chars_len <= 2:
        return out1 == out2
    else:
        if out1 == out2:
            return palindrome(characters[1:-1])
        else:
            return False



if __name__ == '__main__':
    print(palindrome('Rats live on no evil star'))

了解裝飾器的本質是

@f
def g(args)

=>

f(g)(args)

將congratulate()函數移至正在裝飾的函數上方(回文)。

我知道我參加晚會很晚,但我想擴大規模。

如前所述,在這種情況下, NameError是由您在實際創建名稱之前使用名稱引起的。 congratulate()移到頂部可解決此問題。


NameError Appart中,您有兩個與裝飾器/功能功能有關的隱式邏輯錯誤


首要問題:

  • congratulate您的if子句始終求值為True ; 當字符串是回文符時,您並不會完全祝賀您。

這是由於以下事實造成的:函數對象始終求值為True ,因此條件形式為if func:的條件將始終執行:

def f(): 
    pass
if f: 
    print("I'm true!")  
# Prints: I'm true!

值得慶幸的是,這是微不足道的,並且可以通過實際調用if func("test string"):函數來輕松解決if func("test string"):


第二期:

  • 這里的第二個問題不那么瑣碎,可能是由於裝飾者可能會感到困惑。 您實際上並沒有按照應該使用裝飾器的方式使用congratulate()

裝飾器是一個可調用函數, 返回調用函數(可調用函數是諸如函數,在__call__重載的類之類的東西)。 您的“裝飾器”在這里所做的只是簡單地接受一個函數對象,評估該對象是否為True ,然后打印出祝賀。

最糟糕的部分? 它還隱式地將palindrome名稱重新綁定為None

同樣,您可以在下一個代碼段中看到這種間接效果(押韻為+1):

def decor(f):
     if f: print("Decorating can be tricky")        

@decor
def f(): 
    print("Do I even Exist afterwards?")

# When executed, this prints:
Decorating can be tricky

很酷,我們的函數f已經過修飾,但是,看看我們嘗試調用函數f會發生什么:

f()
TypeError                                 Traceback (most recent call last)
<ipython-input-31-0ec059b9bfe1> in <module>()
----> 1 f()

TypeError: 'NoneType' object is not callable

是的,我們的函數對象f現在已分配給了decor函數的返回值None

發生這種情況是因為@syntax@syntax直接等效於以下內容:

@decor
def f(): pass

# similar to 
f = decor(f)  # we re-assign the name f!

因此,我們必須確保裝飾器的返回值是一個可以隨后再次調用的對象ergo,這是一個可調用的對象。


所以你會怎么做? 您可能會考慮的一種選擇就是簡單地返回您傳遞的函數:

def congratulate(func):
    if func("A test Phrase!"):
        print('Congratulations, it\'s a palindrome!')
    return func

這將確保裝飾器在您的palindrome()函數上運行之后,名稱palindrome仍將映射到可調用對象。

問題? 事實證明這是一次性的 當Python遇到您的裝飾器和函數時,它將執行一次 congratulate ,因此僅執行一次 if子句。

但是, if 每次調用函數,都需要它來運行它! 為了做到這一點,您能做什么? 返回執行裝飾功能 (所謂的嵌套函數裝飾器)的功能

這樣,您將為名稱palindrome創建一個新函數,並且該函數包含您的原始函數,該函數確保每次調用palindrome()時都會執行該函數。

def congratulate(func):  # grabs your decorated function
    # a new function that uses the original decorated function
    def newFunc():
        # Use the function
        if func("Test string"):
            print('Congratulations, it\'s a palindrome!')
    # Return the function that uses the original function
    return newFunc

newFunc現在是一個函數,它發出對原始函數的調用。

裝飾過程現在將palindrome名稱分配給newFunc對象(注意我們如何通過return newFunc返回它。

結果,每次執行palindrome()形式的調用時,該調用都會轉換為newFunc() ,后者又在其主體中調用func() (如果你仍然和我在一起,我贊揚你)。


最后的問題是什么? 我們已經對func的參數進行了硬編碼。 newFunc() ,每次調用palindrome()函數newFunc()都會使用func("Test String")的調用簽名調用原始函數func ,這不是我們想要的,我們需要能夠傳遞參數。

有什么解決方案? 幸運的是,這很簡單:將參數傳遞給newFunc() ,然后將其傳遞給func()

def congratulate(func):  # grabs your decorated function
    # a new function that uses the original decorated function
    # we pass the required argument <phrase>
    def newFunc(phrase):
        # Use the function
        # we use the argument <phrase>
        if func(phrase):
            print('Congratulations, it\'s a palindrome!')
    # Return the function that uses the original function
    return newFunc

現在,每當您調用palindrome('Rats live on no evil star')這都會轉換為對newFunc('Rats live on no evil star')調用,該調用將以func('Rats live on no evil star') if子句中func('Rats live on no evil star')

執行后,這將非常有效,並為您帶來所需的結果:

palindrome('Rats live on no evil star')
Congratulations, it's a palindrome!

希望您喜歡閱讀,我相信我已經完成了!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM