iT邦幫忙

2

python中的閉包&裝飾器 提高程式碼品質的神器

  • 分享至 

  • xImage
  •  

前言

Python是一種高級程式語言,提供了許多強大的功能和工具,其中包括閉包(closure)和裝飾器(decorator)。這些功能可以幫助各位更有效地撰寫程式碼,並使程式碼變得簡潔。本文將探討Python中的閉包和裝飾器,並解釋它們如何工作以及我如何使用它們。

以上皆為本人開發經驗分享,若有更多應用也歡迎提出!

閉包(closure)

閉包是一種函數編程的概念,它是指在內部函數中引用了外部函數的變量,並且外部函數已經返回了內部函數。這種情況下,內部函數將保留對外部函數變量的引用,即使外部函數已經完成執行。這種引用關係就形成了閉包。閉包可以捕獲並存儲其所引用的變量的值,這些值將在內部函數被調用時使用。這聽起來像在繞口令,寫起來更像在證明複雜的數學定理QQ,我們直接來看看程式部分是怎麼撰寫的吧!

首先先來個簡單的例子,我們透過這個概念定義一個相加的程式:

def plus_x(x):
    def plus_y(y):
        return x + y
    return plus_y

add_5 = plus_x(5)
add_10 = plus_x(10)

print(add_5)  <function outer_func.<locals>.inner_func at 0x0000028F529F49D0>
print(add_10)  <function outer_func.<locals>.inner_func at 0x0000028F52B0EAF0>

print(add_5(2))  # 7
print(add_5(4))  # 9
print(add_10(4))  # 14

我們可以看到我們定義了一個相加的程式,add_5 = plus_x(5)也是一個副程式,這個副程式接受一個輸入,並將輸入+5後輸出,另外我們也定義一個可以+10的副程式。但add_5本質上還是一個函式,所以直接print的時候會發現它是一個函式物件,那在使用add_5這個函式的時候會呼叫plus_y(y)的部分,從而輸出最終結果。

以我的理解簡單來說,閉包就是讓我們可以定義多個功能類似的副程式(本例子是定義+5跟+10的副程式),透過外部程式的參數微調整個副程式的功能,就不用浪費篇幅去寫兩個類似的功能了。讀者也可以建立幾個以一個數字為參數輸入的普通副程式,並比較看看差異~

裝飾器(decorator)

裝飾器是一種Python函數,它可以修改另一個函數的行為,而無需修改原本副程式的程式碼。裝飾器通常被用於在不修改函數的程式碼的情況下添加額外的功能或行為(有點像給鋼彈換上裝備一樣)。裝飾器可以像普通函數一樣被調用,並將另一個函數作為參數進行修改。裝飾器通常使用@符號標記在函數的定義之前。

也是一樣直接來看範例。今天我想測試每個程式執行的速度,但不希望改到原本副程式的程式碼,我們就可以這樣寫:

from time import time
def timing(func):
    def wrapper():
       start=time()
       func() #執行被裝飾的函數
       end=time()
       print(f"used time: {end-start}")
    return wrapper

@timing #使用裝飾器裝飾sumfunc()這個副程式
def sumfunc():
    s=0
    for i in range(100000):
        s+=i
    print(s)

sumfunc()

# 4999950000
# used time: 0.00461125373840332

首先,裝飾器會用到閉包的概念,我們定義了一個計時的副程式timing,這個外部函式會接受一個函式輸入(這很重要),底下實際呼叫sumfunc()的時候,其實是執行wrapper()(因為sumfunc()使用timing去裝飾,新增了一個計時功能,所以實際執行的時候是呼叫timing這個部分),接著這個外部函式接受的函式輸入(也就是sumfunc())就會在wrapper()中被執行,並輸出結果。
也因為我們宣告的副程式(sumfunc())實際上會在wrapper()中被執行,所以有一些回傳值的處理就要在wrapper()中處理了。我們改寫一下程式:

from time import time
def timing(func):
    def wrapper():
       start=time()
       result = func() #執行被裝飾的函數
       end=time()
       print(f"used time: {end-start}, result:{result}")
    return wrapper

@timing #使用裝飾器裝飾sumfunc()這個副程式
def sumfunc():
    s=0
    for i in range(100000):
        s+=i
    return s
    
sumfunc()

# used time: 0.005532264709472656, result:4999950000

我們可以看到,從wrapper()中取得副程式的結果,最後再print出來。除此之外裝飾器也有很多用途。希望各位能好好的活用它~

結語

這兩個功能是我最近在開發深度學習的一些專案時,覺得很好用的方法(另外一點是寫起來感覺很帥XD)。想藉此機會分享給大家,但小弟我才疏學淺,若有錯誤也歡迎指正!
接下來沒意外我會分享如何在keras中使用這兩個概念建立神經網路層(functional api的寫法就很類似)。再寫生成對抗網路(GAN)這種類型,模型較複雜的程式就可以用到。

另外有一些提問與未詳細說明的,我會在另一篇補充更詳細的說明喔~
python中的裝飾器小補充

這些功能其實較冷門,所以具體在寫程式都還是要看自己的習慣喔!


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言