Skip to main content

Decorator in python3

裝飾器

寫程式很常看到高級技巧裝飾器,@xxxx 這種東西,因為之前都可以用一些方式避開不這樣寫,直到某次在跟別人串程式的時候,為了減少影響範圍,所以選用裝飾器去拿對方 return 的東西在處理一次,而對方也很簡單,只要把裝飾器放上去就可以直接用了,很方便!裝飾器主要應用情境是在不影響對方 function 下,給這個 function 新功能,裝飾器的彈性和可擴展性使其成為 Python 中強大的程式設計工具之一.

partial function

因為裝飾器有時候會配合 partial function,所以要了解一下.

partial 是 python 的偏函示,使用上感覺就是方便你帶入預設參數到一個已知 function,並透過偏函式去建立新的 function!使用 partial 函數可以非常方便地對現有函式進行定制和重用。那為何不預設帶入?總會有需要多個預設值的情況吧~

  • 偏函示範例一
from functools import partial

# 原始函式
def add(x, y):
return x + y

# 創建偏函式
add_five = partial(add, y=5)

# 使用偏函式
result = add_five(10)
print(result) # 輸出: 15
  • 偏函示範例二
from functools import partial

# 定義一個具有多個參數的函式
def multiply(x, y, z):
return x * y * z

# 使用偏函數將其中一個參數的預設值固定為 2
new_func = partial(multiply, y=2)

# 調用新函式
result = new_func(3, z=4)
print(result) # 輸出: 24

裝飾器正篇

from functools import partial, wraps
import vt

def partial_decorator1(func=None, *, apikey=api_key_global):
def actual_decorator(func, apikey):
@wraps(func)
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
sha256_hash = result
client = vt.Client(apikey)
file = client.get_object(f"/files/{sha256_hash}")
print(file.last_analysis_stats)
client.close()
return result

return wrapper

if func:
return actual_decorator(func, apikey)
else:
return partial(actual_decorator, apikey=apikey)

# 使用新的装饰器
@partial_decorator1(apikey=api_key_global)
def my_function(file_path):
# 在这里执行你的逻辑并返回一个哈希值
return file_path

result = func(*args, **kwargs)這邊會拿到原始 function 帶入參數的一般 return!後面的程式碼是對該 return 作處理!

最後面的 if else 會比較難懂.

  • if func: 這部分處理的是當裝飾器被用於一個函數時(也就是被直接應用到函數上,例如 @partial_decorator1)的情況。在這種情況下,func 參數會被自動設置為被裝飾的函數。這時,actual_decorator(func, api_key) 會被調用,其結果(也就是裝飾過的函數)被返回。

  • else: 這部分處理的是當裝飾器被當作函數調用(例如 @partial_decorator1() 或 @partial_decorator1(api_key=some_key))的情況。在這種情況下,func 參數會默認為 None,因此 else 分支會被執行。 partial(actual_decorator, api_key=api_key) 創建了一個新的裝飾器,這個裝飾器會接受一個函數作為參數,並返回該函數的裝飾版本。新的裝飾器會在稍後的時間被調用,這時才會裝飾目標函數。

當裝飾器以 @partial_decorator1 的形式應用到函數上時,會走 if func: 分支;而當裝飾器以 @partial_decorator1() 或 @partial_decorator1(api_key=some_key) 的形式應用時,會走 else: 分支

裝飾器範例二

from functools import partial, wraps

def partial_decorator1(generator_func=None, *, api_key="api_key_global"):
def actual_decorator(generator_func, api_key):
@wraps(generator_func)
def wrapper(*args, **kwargs):
# 打印字串
print("Before generator execution")
# 並不會執行,只是產生生成器而已
result = generator_func(*args, **kwargs)
print(api_key)
# 打印字串
print("After generator execution")
return result

return wrapper

if generator_func is None:
return partial(actual_decorator, api_key=api_key)
else:
return actual_decorator(generator_func, api_key)


# @partial_decorator1(api_key="my_api_key"),這是代表裝飾器被當作函式調用情況,
# 因為調用時沒有帶generator_func,所以generator_func會是預設的None.會走if那邊,
# 用partial(actual_decorator, api_key=api_key),產生新裝飾器,這個裝飾器會接受一個函數作為參數,並返回該函數的裝飾版本。
@partial_decorator1(api_key="my_api_key")
def my_generator():
yield 1
yield 2
yield 3

# 使用装饰器修饰生成器函数,這邊是在修改帶入的既有餐數,api_key="my_api_key"
# 注意這邊的my_generator()是返回<generator object my_generator at 0x10041ce00>
# yield是實現generator的一種方法!
for value in my_generator():
print(value)

# 使用 partial_decorator1 创建装饰器,@partial_decorator1不帶參數,就會直接應用到function
# ,因此參數generator_func就會有帶東西,而走else.else做的事情是修飾並return新function
# actual_decorator(generator_func, api_key)會修飾function,return新function.
@partial_decorator1
def my_generator1():
yield 1
yield 2
yield 3

# 使用装饰器修饰生成器函数,這邊是如果什麼都不帶,就會用默認的api_key="api_key_global"
for value in my_generator1():
print(value)

小結

一般裝飾器不帶括號(ex.@partial_decorator1),代表該裝飾器會自動將 func 參數設定為套用的 func,這滿單純! 如果有帶括號(ex.@partial_decorator1(api_key=some_key)),會需要指定帶入的 function,就需要透過 partial 寫法去指定要帶入哪個 function 作為裝飾器,不過都用裝飾器了,當然會帶入要執行的包裝 function.

另外,裝飾器寫法很多,這邊寫的只是其中一種!

info

裝飾器主要應用情境

  • 記錄日誌:裝飾器可以捕獲函式的輸入和輸出,並將其記錄到日誌中,以便後續調試和追蹤。

  • 身份驗證和授權:裝飾器可以在執行函式之前檢查用戶的身份驗證和權限,以確保只有授權的用戶可以訪問該函式。

  • 快取:裝飾器可以將函式的輸出快取起來,以減少重複計算,提高效能。

  • 異步處理:裝飾器可以將函式的執行轉換為非同步處理,以提高系統的反應性和併發能力。

  • 計時和性能監控:裝飾器可以計時函式的執行時間,並收集性能指標,用於分析和優化代碼的效能。