Python decorator


上週面試被問到 python decorator,這下尷尬了......
我只會用但我不會寫 decorator,雖然在引導下有做出來,但是......好吧,希望我可以被錄取,拜偷。有錄取我明年就投稿 COSCUP 跟 MOPCON!(發願)


因為對 decorator 的概念不熟,所以趕緊找時間惡補一下,發現其實並不難理解。

可以簡單理解成將 function A 丟到 function B 裡面,得到的結果是 function A 再加上 function B。

只不過這個 function A 可以由任何的 function 取代,也就是丟進去的 function 可以是任何一個 function,而結果都會被 function B 包過一層。

餵進去的是 function,吐出來的也是 function。

因為是寫給自己看的所以就沒什麼潤飾了

我們先寫一個自我介紹的 func,他會回傳一個字串裡面寫著 "HI, I'm {your_name}",如果沒有傳入任何參數,那預設你就叫做 john。

def introduceMySelf(name="john"):
    introduction = f"""HI, I'm {name}"""
    return introduction

if __name__ == "__main__":
print(introduceMySelf())
# HI, I'm john

因為我是個比較害羞怕生的人,所以我在說出自己名字之前會先發出 zmmmm 的聲音。這邊我用一個裝飾子幫我做發出 zmmmm 的聲音。

def sayHello(func):
    # inner_function
    def wrapper(*args, **kwargs):
        print("Zmmmm.....")
        res = func(*args, **kwargs)
        return res

    return wrapper

裝飾子的內容是這樣。我們在 func 中再寫一個 func,他會把傳入的 func 外面再包上其他東西後丟回去,最後整個 func 就會丟出一個 wrapper 的 inner function(所以會說餵進去的是 func,吐出來的也是 func)。

我們把裝飾子拿來用就變成:

def sayHello(func):
    # inner_function
    def wrapper(*args, **kwargs):
        print("Zmmmm.....")
        res = func(*args, **kwargs)
        return res

    return wrapper


@sayHello
def introduceMySelf(name="john"):
    introduction = f"""HI, I'm {name}"""
    return introduction


if __name__ == "__main__":
    print(introduceMySelf())
    # Zmmmm.....
    # HI, I'm john

面試時還有被問到 list 跟 dict 的 unpack,其實知道怎麼用,但不知道為什麼當下居然愣住,只想到 dict 的 unpack 該怎麼使用,總之順便記下來。

def wrapper(*args, **kwargs):
    print("Zmmmm.....")
    res = func(*args, **kwargs)
    return res

在 wrapper 中用 *args 接受 list 型別的物件,**kwargs 接受 dict 型別的物件。
所以丟入 list 型別的物件可以處理:

print(introduceMySelf("jojo"))

這是代表說其實丟進去的值是一個 list 的型別,進去之後會自動 unpack 這個 list 拿到參數。

丟入 dict 型別的物件時則可以準確抓取鍵值:

print(introduceMySelf(name="mary"))

這是說丟進去是一個 dict 的型別,進去之後會自動 unpack 這個 dict 拿到參數,後續就可以針對特定的 parameter 做處理。

完整範例程式法可以參照

def sayHello(func):
    # inner_function
    def wrapper(*args, **kwargs):
        print("Zmmmm.....")
        res = func(*args, **kwargs)
        return res

    return wrapper


@sayHello
def introduceMySelf(name="john"):
    introduction = f"""HI, I'm {name}"""
    return introduction


if __name__ == "__main__":
    print(introduceMySelf())
    # Zmmmm.....
    # HI, I'm john

    print()

    print(introduceMySelf("jojo"))
    # Zmmmm.....
    # HI, I'm jojo

    print()

    print(introduceMySelf(name="mary"))
    # Zmmmm.....
    # HI, I'm john

    print()

    print(introduceMySelf.__name__)
    # wrapper

在上一個範例中最後一行可以發現我們印出來的值是 wrapper。
為什麼?不是應該是 introduceMySelf 才對嗎?
其實我也不知道

不過 python 有內建一個 wraps 的工具,只要修改一些小地方就可以了。

from functools import wraps


def sayHello(func):
    # inner_function
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("Zmmmm.....")
        res = func(*args, **kwargs)
        return res

    return wrapper


@sayHello
def introduceMySelf(name="john"):
    introduction = f"""HI, I'm {name}"""
    return introduction


if __name__ == "__main__":
    print(introduceMySelf())
    # Zmmmm.....
    # HI, I'm john

    print()

    print(introduceMySelf("jojo"))
    # Zmmmm.....
    # HI, I'm jojo

    print()

    print(introduceMySelf(name="mary"))
    # Zmmmm.....
    # HI, I'm john

    print()

    print(introduceMySelf.__name__)
    # introduceMySelf

感謝有看到最後的你,雖然我不知道你看不看得懂我在寫什麼。
如果覺得有幫助到你,還請幫我按讚、訂閱、按下小鈴鐺唷!
或是給我一份工作

#Python #decorator






你可能感興趣的文章

基本屬性注入方法

基本屬性注入方法

WPF加入Resource之語系功能

WPF加入Resource之語系功能

[Day 05] 走訪器模式,建造者模式,責任鏈模式,解譯器模式

[Day 05] 走訪器模式,建造者模式,責任鏈模式,解譯器模式






留言討論