iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 10
8

裝飾器能做什麼

相信很多讀者都聽過AOP,所以筆者在此就不加贅述了,而裝飾器就是實現AOP的一種編寫程序。簡單解釋裝飾器的功用就是,再不更改程序的狀況下能夠附加功能。在介紹如何實作裝飾器前,先告訴大家如何使用。

本文章同步放置於此

使用裝飾器

大家先看一下下列例子:

from flask import Flask

app = Flask(__name__)
@app.route("/")
def hello():
    return "Hello World!"

if __name__ == "__main__":
    app.run()

相信各位讀者對於這例子應該不陌生,沒錯這就是第一天介紹給大家的例子,而裝飾器在哪呢,@app.route("/")這就是裝飾器,因為有它Python可以寫的更簡潔。

實作一個裝飾器

在認識如何使用裝飾器後要介紹大家如何自訂一個裝飾器,請各位讀者看看以下例子:

def printHello(func):
    def wrapper():
        print('Hello')
        return func()
    return wrapper

@printHello
def printWorld():
    print('World')

printWorld()

如此第一個裝飾器完成了,相信大家應該知道執行後會顯示以下內容:

Hello
World

就這樣我們建立了一個會印出Hello的裝飾器,所以當我們需要一個印出Hello Kitty的函式該如何寫呢?相信許多讀者已經有答案了,如果還沒有想法請看以下例子:

@printHello
def printKitty():
    print('Kitty')

printKitty()

裝飾器的原理

裝飾器是怎樣一個語法,為什麼他能夠這樣寫,再來就是他這樣寫是代表甚麼呢?眼尖的讀者可以看到一個特別的符號@這就是人稱Python@語法糖的東西,它可以讓你的程序變得簡潔,而@是代表甚麼呢?請見以下例子:

printWorld = printHello(printWorld)

上述可以說明@的功用就是將printWorld丟到printHello函式中,而我們之前定義printHello函式就是將丟進來的參數printWorld包裝後傳出去。所以這邊最外面的printWorld所接收到的東西就是包裝後的wrapper(),所以呼叫printWorld時其實是呼叫wrapper()所以會先印出Hello在執行方法printWorld。相信這樣的解釋可以讓大家對於裝飾器有更進一步的了解。

函式的參數怎麼辦

如果說裝飾器所裝的函式需要輸入參數該如何處理呢,換句話說想要整合printKitty跟printWorld該如何處理呢?相信經過上述的介紹後應該不難處理,不過如果讀者們不知道如何處理請看下列例子吧:

def printHello(func):
    def wrapper(arg):
        print('Hello')
        return func(arg)
    return wrapper

@printHello
def printArg(arg):
    print(arg)

printArg('World')
printArg('Kitty')

就這樣更進一步的裝飾器完成了,如此裝飾器可以把參數傳達給所定義的函式。

函式的參數很複雜怎麼辦

如果裝飾器所掛載的函式參數很複雜時該如何處置呢,總不能定了一堆類似的裝飾器吧。所以這裡介紹一個通吃的參數輸入定義方式,請見以下例子:

def printHello(func):
    def wrapper(*args, **kwargs):
        print('Hello')
        return func(*args, **kwargs)
    return wrapper

@printHello
def printSingle(arg):
    print(arg)

@printHello
def printDouble(arg1, arg2):
    print(arg1)
    print(arg2)

printSingle('World')
printDouble('Kitty', 'Danny')

其中*args, **kwargs是甚麼東西啊?請看下列說明。

*args與**kwargs

其中*args是一個list指的是不論個數的輸入參數,**kwargs是一個dict指的是任何名子的參數,更進一步請看下列說明:

def func(*args, **kwargs):
    for item in args:
        print("I'm args and value is {0}".format(item))
    for key, value in kwargs.items():
        print("I'm kwargs and key and value is {0}={1}".format(key, value))


func([{
    'name': 10,
    'value': 20
}, 1, 2, 3], 12364)

func(name=1023)

結果顯示如下

"I'm args and value is [{'name': 10, 'value': 20}, 1, 2, 3]"
"I'm args and value is 12364"
"I'm kwargs and key and value is name=1023"

透過這兩個參數可以指定所有的輸入參數,如此裝飾器的功能更強大了,能夠用在更多地方了。

裝飾器也能加參數

處理完掛載的裝飾器之後,如果想要在裝飾器上輸入參數該怎麼辦呢,就看看下列例子吧:

def printArg(arg):
    def decorator(func):
        def wrapper(*args, **kwargs):
            print(arg)
            return func(*args, **kwargs)

        return wrapper

    return decorator

@printArg('Hi')
def sayHiAndPrintArg(arg):
    print(arg)


sayHiAndPrintArg('World')

小結

沒想到光一個裝飾器一天還不夠講,明天接續講裝飾器進階的用法,敬請期待。


上一篇
DAY08-搞懂Python的OOP
下一篇
DAY10-搞懂Python裝飾器的進階內容
系列文
瓶子裡裝甚麼藥,使用Flask輕輕鬆鬆打造一個RESTful API31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

2 則留言

0
r567tw
iT邦研究生 5 級 ‧ 2018-10-26 12:42:46

不知道我的理解有沒有正確
大大範例中

@printHello
def printKitty():
    print('Kitty')

printKitty()

意思是 printHello 為printKitty的裝飾器
所以在最後printkitty 時原本只輸出kitty,但也會先呼叫printHello 所以變成‘hellokitty’?

是這樣嗎?

kirai iT邦新手 4 級 ‧ 2018-10-26 12:50:14 檢舉

上面的例子是先執行print('hello')在執行print('kitty')所以結果如下

hello
kitty

不知道有沒解決你的問題
不過裝飾器也可以先執行print('kitty'),在執行print('hello')就取決於你裝飾器怎麼實作。
希望有解決到你的問題

r567tw iT邦研究生 5 級 ‧ 2018-10-26 16:57:37 檢舉

哇! 這個裝飾器好玩
我自己也在寫python, 但今天終於知道flask那樣會什麼可以work了~ 裝飾器感覺讓python語法又更活了~

0
krarm
iT邦好手 1 級 ‧ 2022-06-28 15:25:55

寫技術文章不用自己問自己,拖字數而已
平鋪直接講就好

我要留言

立即登入留言