Pin-Jiun / Python

Python Document
0 stars 0 forks source link

21-Decorator #21

Open Pin-Jiun opened 1 year ago

Pin-Jiun commented 1 year ago

Decorator

其功能是用來輔助其他function的運作 Decorator也是一個function, 所以最外圍的語法和def function相同

#定義裝飾器
def decorator(callbacl_function):
    def inner_function():
        #裝飾器內部程式碼
        callbacl_function()
    return inner_function

#裝飾器輔助的function
@decorator
def f():
    #function code
    pass

#呼叫帶有裝飾器的function
f()
#定義裝飾器
def test_decorator(callbacl_function):
    def inner_function():
        #裝飾器內部程式碼
        print("裝飾器程式範例")
        callbacl_function()
    return inner_function

#裝飾器輔助的function
@test_decorator
def f():
    #function code
    print("普通function")

#呼叫帶有裝飾器的function
f()
裝飾器程式範例
普通function

執行順序: 1.呼叫function f() 2.此時發現@ 裝飾器特殊語法, 會先跳至裝飾器 3.f 會被當成parameter傳至callbacl_function 4.執行裝飾器內部的code :inner_function() 5.執行完畢, 藉由callbacl_function()跳回原本的 f()


再加上參數

#定義裝飾器
def test_decorator(callbacl_function):
    def inner_function():
        #裝飾器內部程式碼
        print("裝飾器程式範例")
        callbacl_function(6)
    return inner_function

#裝飾器輔助的function
@test_decorator
def f(data):
    #function code
    print("普通function, data=", data)

#呼叫帶有裝飾器的function
f()
裝飾器程式範例
普通function, data= 6

實際應用例子

#定義一個裝飾器, 計算1+2+...+50的總和
def calculate(cb):
    def f():
        #裝飾器內部程式碼
        print("裝飾器程式範例")
        result = 0
        for i in range(51):
            result += i
        cb(result)
    return f

#裝飾器輔助的function
@calculate
def show(result):
    #function code
    print("計算結果是", result)

@calculate
def show_in_eng(result):
    #function code
    print("Result is", result)

#呼叫帶有裝飾器的function
show()

show_in_eng()
裝飾器程式範例
計算結果是 1275
裝飾器程式範例 
Result is 1275 
Pin-Jiun commented 1 year ago

decorators 多層的話是採 ”recursive” 的方式處理,如果一個 function 有兩個以上的 decorators ,邏輯上則會先合併「最靠近」的 decorator 吐出新的 function 再由上面一個的 decorator 吃進去!


def print_func_name(func):
    def warp_1():
        print("Now use function '{}'".format(func.__name__))
        func()
    return warp_1

def print_time(func):
    import time
    def warp_2():
        print("Now the Unix time is {}".format(int(time.time())))
        func()
    return warp_2

@print_func_name
@print_time
def dog_bark():
    print("Bark !!!")

if __name__ == "__main__":
    dog_bark()
    # > Now use function 'warp_2'
    # > Now the Unix time is 1541239747
    # > Bark !!!

所以 dog_bark() 會先被 @print_time 吃進去,然後吐出一個叫做 wrap_2 的function,而這個 warp_2 function 又會被 @print_func_name 吃進去,吐出一個叫做 wrap_1 的 function。

所以最後執行的結果順序是先 print Now use function 'wrap_2' 再 print Now the Unix time is 1541239747 。而且你會發現由於最外層的 @print_func_name 真正吃進去的 function 是已經被 @print_time 修飾過的 function,所以 print 的是Now use function 'wrap_2' 而不是 Now use function 'dog_bark' !!

ref

Pin-Jiun commented 1 year ago

變數名 與 函式物件 (object)

def foo():
    return "bar"

如果我們 print(foo),會得到 “function foo at 0x1028831e0” 表示 foo 是一個變數名,並且指向一個函式物件 (object) 。 如果要調用函式的話,則加上(), foo(),即可調用函式。

def foo():
    return "bar"

print(foo) # 變數名
print(foo()) # 函式物件

裝飾詞 decorator

首先我們在 def timer(func): 將 func 變數名稱傳入,接下來定義 def wrap(sleep_time) 函式,並且在裡面將剛剛傳入的 func(sleep_time) 調用,這樣就完成一個簡單的裝飾詞囉!

import time

def timer(func):
    def wrap(sleep_time):
        t_start = time.time()
        func(sleep_time)
        t_end = time.time()
        t_count = t_end - t_start
        print('[花費時間]', t_count)

    return wrap

def dosomething(sleep_time):
    print('do some thing')
    time.sleep(sleep_time)

foo = timer(dosomething)
foo(3)

>>> do some thing
>>> [花費時間] 3.004279136657715

語法糖 (Syntax Candy)

如果不想寫成 foo = timer(dosomething),只需要加上 @timer,並直接調用 dosomething() 函式執行


def timer(func):
    def wrap(sleep_time):
        t_start = time.time()
        func(sleep_time)
        t_end = time.time()
        t_count = t_end - t_start
        print(‘[花費時間]’, t_count)

    return wrap

@timer
def dosomething(sleep_time):
    print(‘do some thing’)
    time.sleep(sleep_time)

dosomething(3)

ref