iT邦幫忙

2021 iThome 鐵人賽

DAY 5
0

Decorator(裝飾器) 是 Python 中很好用的一個東西,只要 @ 一下就可以處理掉很多東西,其實 Decorator(裝飾器) 不難理解,而且容易使用所以在 Python 中很常使用到。不過這篇沒有要講太深,只是讓你比較容易理解與使用而已。

裝飾器-基本使用

那麼這麼好用的東西怎麼使用呢?就讓我們自己做一個簡單的出來吧!就先來刻個計算程式執行時間的裝飾器好了,這樣可以更快的了解。首先架構為這樣(就一個資料夾裡面有個檔案而已啦,算不上架構):

decorator_test/
└── test.py

檔案內容為:

test.py

from time import time


def time_counter(func):
    def wrapper(*args, **kwargs):  # 習慣上會取名叫 wrapper
        start = time()
        result = func(*args, **kwargs)
        end = time()
        usage_time = (end - start)
        return result, usage_time

    return wrapper


# 不要括號喔
@time_counter
def star_triangle(times):
    step = ""
    for i in range(1, times+1, 2):
        for j in range((times-i)//2):
            step += " "
        for j in range(i):
            step += "*"
        step += "\n"
    return step


res = star_triangle(5000)  # 5000 如果跑太久可以適當的減少它
print("Time: {:>30.24f} second".format(res[1]))

執行後的時間大概會長這樣(嗯,我沒有讓它輸出,不然很恐怖)

> Time:     3.177063703536987304687500 second

蛤,這樣就一個喔,啊它到底是怎樣出現的?

別急,把15行刪掉(@time_counter 那行),再把27行改成這樣

res = time_counter(star_triangle)(5000)  # 5000 如果跑太久可以適當的減少它

應該會出現類似的結果(秒數不一樣很正常)

> Time:     3.209057331085205078125000 second

這樣懂了嗎?還記的 Day 06 講過的這些嗎?

在 Python 中,Function 的地位和 C++、Java等不同,是屬於 "First-class Citizen" (一等公民),故稱作 "First-class function" (一級函數)。

什麼是 "First-class Citizen" 呢?跟二等公民又差在哪呢?

最明顯的差別是 Python 中的 Function 可以當成參數傳遞並執行(對啦,我就是只想講這句而已,其他暫時不太重要)。

Decorator(裝飾器) 就是把@下面的 Function 當成參數傳遞並執行了,大概就是每次呼叫 Function 都會像這樣一層一層轉換(這邊只是方便理解而已):

Step1: res = star_triangle(5000)
Step2: res = time_counter(star_triangle)(5000)
Step3: res = wrapper(5000):
Step4: res get wrapper return

因為 func 這個東西位於 time_counter 的裡面,所以同樣位於time_counter 裡面的 wrapper 不需要把它當參數也可以使用(大概就是台中市民搭公車免費,你住在北區的話,你也可以免費的意思)

裝飾器-加入參數

啊如果裝飾器想要有參數的話怎麼辦,就再加一層變這樣

test.py

from time import time


def time_counter(func):
    def wrapper(*args, **kwargs):  # 習慣上會取名叫 wrapper
        start = time()
        result = func(*args, **kwargs)
        end = time()
        usage_time = (end - start)
        return result, usage_time

    return wrapper


def record_now_time(time_format="%Y-%m-%d %H:%M:%S"):
    def decorator(func):
        def wrapper(*args, **kwargs):
            print(datetime.now().strftime(time_format) + " - Start function: " + func.__name__)
            # __name__ 可以取得 Function 的名稱
            result = func(*args, **kwargs)
            print(datetime.now().strftime(time_format) + " - End function: " + func.__name__)
            return result

        return wrapper

    return decorator


@record_now_time(time_format="%H:%M:%S")
def star_triangle(times):
    step = ""
    for i in range(1, times+1, 2):
        for j in range((times-i)//2):
            step += " "
        for j in range(i):
            step += "*"
        step += "\n"
    return step


res = star_triangle(5000)  # 5000 如果跑太久可以適當的減少它

這樣執行的輸出會變這樣(最後面的print要刪掉)

> 22:20:26 - Start function: star_triangle
> 22:20:29 - End function: star_triangle

裝飾器-多層

如果想要同時使用多個裝飾器怎麼辦,就加上去啊,會變成這樣:

test.py

from time import time


def time_counter(func):
    def wrapper(*args, **kwargs):  # 習慣上會取名叫 wrapper
        start = time()
        result = func(*args, **kwargs)
        end = time()
        usage_time = (end - start)
        return result, usage_time

    return wrapper


def record_now_time(time_format="%Y-%m-%d %H:%M:%S"):
    def decorator(func):
        def wrapper(*args, **kwargs):
            print(datetime.now().strftime(time_format) + " - Start function: " + func.__name__)
            result = func(*args, **kwargs)
            print(datetime.now().strftime(time_format) + " - End function: " + func.__name__)
            return result

        return wrapper

    return decorator


@time_counter
@record_now_time(time_format="%H:%M:%S")
def star_triangle(times):
    step = ""
    for i in range(1, times+1, 2):
        for j in range((times-i)//2):
            step += " "
        for j in range(i):
            step += "*"
        step += "\n"
    return step


res = star_triangle(5000)  # 5000 如果跑太久可以適當的減少它
print("Time: {:>30.24f} second".format(res[1]))

然後它就會一層一層吃上去,類似這樣:

Step1: res = star_triangle(5000)
Step2: res = record_now_time(time_format="%H:%M:%S")(star_triangle)(5000)
Step3: res = time_counter()(record_now_time)(time_format="%H:%M:%S")(star_triangle)(5000)
Step4: res = wrapper(5000):
Step5: res get wrapper return

有沒有發現到後面越來越長了,寫個裝飾器更加的簡單明瞭對吧!

參考資料

Python進階技巧 (3) — 神奇又美好的 Decorator ,嗷嗚!

搞懂Python的裝飾器

那麼就大概這樣,這篇是讓你在使用套件時,遇到裝飾器不會不知如何使用而已。話說其實不只 Funciton 可以變成裝飾器,Class 也可以。不過關於這個問題,我不會在下個文章講解(這是一個小坑了,真要講大概又要開幾篇來講,話說都第五天了還沒進主題)。

大家掰~掰~


上一篇
Day 04 Python 進階
下一篇
Day 06 Python 的特點
系列文
月光下的Flask之旅30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言