iT邦幫忙

2023 iThome 鐵人賽

DAY 27
0

在了解完Python的基本語法與功用後,今日我們將要來探討Python中的函式修飾器,因為在後續有關網頁的flask中會使用到該方法,提前了解之後便能更好上手!

  • 一般的函式:

怕各位仍不太熟悉Python的函式,在此先做一個範向您展示:

def sum(a,b):
    result = float(a)+float(b)
    return result

sum(8,7)

上面的程式碼,是Python最一般的function範例 ── 一個將傳入數值相加的function。
https://ithelp.ithome.com.tw/upload/images/20231012/20160488NsXE6laCKF.png

一個Python的函式都具有傳入值、回傳值,以及執行的動作。

  • 函式修飾器@Decorator:

在介紹函式修飾器前,我們要提到function的基本屬性。

如果我們為function添加了type()函式,會發現,其實funtion也是一個物件:

def sum(a,b):
    result = float(a)+float(b)
    return result

print(type(sum))

https://ithelp.ithome.com.tw/upload/images/20231012/20160488OXVWoDhd4W.png

所以我們便可以利用此屬性,將function當作傳入值去代入至別的function的裡面,並執行:

def func():
	print("this is a function!")

def wrapper(function):
	function()

wrapper(func)

那最後wrapper的輸出結果,理所當然地便是:this is a function!
https://ithelp.ithome.com.tw/upload/images/20231012/20160488yRF33y74mC.png

而我們也可以在function裡面寫入function:

def function_generator():
	def new_func():
		print("new函式發動")
	return new_func

test = function_generator()  
test()

https://ithelp.ithome.com.tw/upload/images/20231012/20160488IL67MrtNrG.png

詳細流程解釋如下:
https://ithelp.ithome.com.tw/upload/images/20231012/20160488lJpzDkOtmE.png

這些程式碼執行的過程為:

  1. 執行19行的 **test()**
  2. 往上推,test 是甚麼?它是由 function_generator() 回傳的 **new_func()**
  3. new_func() 發動:print(”new函式發動”)
  4. 輸出結果:new函式發動

不過,當我們每次寫個函式,要執行某事件時,腦迴路都要一直繞來繞去,實在是有夠麻煩的 ── 因此,Python中出現了函式修飾器的語法!

def decoratorTest(func):
    def wrapper():
        print("start")
        func()
        print("end")
    return wrapper

@decoratorTest #函式修飾器發威!
def hello():
    print("Hello")

hello()

https://ithelp.ithome.com.tw/upload/images/20231012/20160488dRzpQYMCzX.png

上圖是兩個功能相同的程式,然而,右圖的程式碼是不是比較簡潔呢?

做個小總結,所以Python的函式修飾器是甚麼?

函式修飾器是一個工具,允許您在不修改原始函式程式碼的情況下動態地改變或擴展函式,以便新增或添加額外的功能。

python函式修飾器的優點:

  1. 模組化和可重用性 ── 修飾器允許您將功能和行為分離,使程式碼更具模組化,並且可以輕鬆地使用修飾器來添加不同函式。
  2. 程式碼乾淨 ── 使用修飾器可以保持原始函式的程式碼乾淨,因為您可以將額外的功能放在獨立的修飾器函式中,而不是將其混合在原始函式中。同時,如果您需要在多個函式中應用到相同的功能,修飾器完全可以減少程式碼的重複。
  3. 動態性高 ── 修飾器允許您在執行時動態修改函式的行為,這對於處理各種需求非常有用。
  4. 可讀性高 ── 使用修飾器能夠使程式碼變得乾淨整潔,也使您能夠更清晰地表達或了解函式的意圖,即意可讀性變高了。
  • 函式修飾器的傳入值:

我們也可以對函式修飾器代入數值,以執行不同的結果。

下方的範例便是將程式執行的次數代入至修飾器內,已達成輸出幾次結果的範例:

def decoratorTest(times):
    def decorator(func):
        def wrapper():
            print("start")
            for i in range(times):
                func()
            print("end")
        return wrapper
    return decorator

@decoratorTest(5) #執行五次
def hello():
    print("Hello")

hello()

https://ithelp.ithome.com.tw/upload/images/20231012/20160488TsNC8uJuLe.png

  • 主函式的傳入值:

如果說我們最主要的 hello() 函式也要傳入值,那我們在函式修飾器裡執行這條函式時,也必須要代入這些數值,否則便會出現錯誤。

也因此,我們需要在wrapper內寫入 *args,**kwargs 語法,並把變數代入至函式內執行:

def decoratorTest(times):
    def decorator(func):
        def wrapper(*args, **kwargs):
            print("start")
            for i in range(times):
                func(*args, **kwargs)
            print("end")
        return wrapper
    return decorator

@decoratorTest(5)
def hello(name):
    print("Hello, " + name)

hello("John")

https://ithelp.ithome.com.tw/upload/images/20231012/20160488vvM1fa3K5r.png

這裡介紹一下甚麼是 *args & **kwargs

***args****kwargs 是Python中的特殊語法,用於處理函式的可變參數。它能讓您傳遞不定數量的位置參數和關鍵字參數給一個函式。

讓我們分別解釋它們的作用:

  1. ***args**(星號引數):
    • ***args** 是一個特殊的參數,它允許您將不定數量的位置參數傳遞給一個函式。
    • ***args** 會將這些位置參數打包成一個元組(tuple)。
    • 您可以在函式中使用這個元組來處理傳遞進來的所有位置參數。
def print_args(*args):
    for arg in args:
        print(arg)

print_args(1, 2, 3, 4)  # 輸出:1 2 3 4
  1. ****kwargs**(雙星號關鍵字引數):
    • **kwargs 是另一個特殊的參數,它允許您傳遞不定數量的關鍵字參數(帶有名稱的參數)給一個函式。
    • **kwargs 會將這些關鍵字參數打包成一個字典(dictionary)。
    • 您可以在函式中使用這個字典來處理傳遞所有進來的關鍵字參數。
def print_kwargs(**kwargs):
    for key, value in kwargs.items():
        print(key, value)

print_kwargs(a=1, b=2, c=3)  # 輸出:a 1  b 2  c 3

其中一個常見的用法便是在函式定義中結合使用 *args**kwargs,這樣可以接受任意數量的位置參數和關鍵字參數,好譬如:

def example_function(arg1, arg2, *args, kwarg1="default", **kwargs):
    # arg1 和 arg2 是必要的位置參數
    # *args 接受任意數量的其他位置參數,存為元組
    # kwarg1 是一個有默認值的關鍵字參數
    # **kwargs 接受任意數量的其他關鍵字參數,存為字典
    pass

這種方式能夠使函式更具彈性,能夠處理各種參數組合,同時保持可讀性。

  • 多個修飾器:
from time import sleep,time
def getTime(func):
    def wrapper():
        start = time()
        func()
        print("執行時間:",time()-start)
    return wrapper

def decoratorTest(func):
    def wrapper():
        print("start")
        sleep(1)
        func()
        print("end")
    return wrapper

@getTime
@decoratorTest
def hello():
    print("hello")

hello()

https://ithelp.ithome.com.tw/upload/images/20231012/20160488YIcHLkvgfZ.png

  • 多個修飾器+修飾器傳入值:
from time import sleep,time
def getTime(func):
    def wrapper():
        start = time()
        func()
        print("執行時間:",time()-start)
    return wrapper

def decoratorTest(times):
    def decorator(func):
        def wrapper():
            print("start")
            for _ in range(times):
                sleep(1)
                func()
            print("end")
        return wrapper
    return decorator

@getTime
@decoratorTest(5)
def hello():
    print("hello")

hello()

https://ithelp.ithome.com.tw/upload/images/20231012/20160488RhV57DrSEl.png


以上,都是我們修飾器大概的基礎原理了,是不是非常燒腦呢?

仍是不太理解的話,其實沒關係,先對這些東西有個大概的概念即可。因為在Flask中我們也不會用到這麼難的函式修飾器,等真的在某些專案中實際碰到了,再去深入瞭解即可。

那今天的Python函式修飾器教學便到此結束,明天我們終於要回歸正題,重返網頁的領域,接觸Flask並帶您實際了解如何運作。那,我們明天見囉!


上一篇
【Day26】Python速成班
下一篇
【Day28】Flask基本架構
系列文
連我阿公都會-手把手教你架網站 30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言