裝飾器
寫程式很常看到高級技巧裝飾器,@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.
另外,裝飾器寫法很多,這邊寫的只是其中一種!
裝飾器主要應用情境
記錄日誌:裝飾器可以捕獲函式的輸入和輸出,並將其記錄到日誌中,以便後續調試和追蹤。
身份驗證和授權:裝飾器可以在執行函式之前檢查用戶的身份驗證和權限,以確保只有授權的用戶可以訪問該函式。
快取:裝飾器可以將函式的輸出快取起來,以減少重複計算,提高效能。
異步處理:裝飾器可以將函式的執行轉換為非同步處理,以提高系統的反應性和併發能力。
計時和性能監控:裝飾器可以計時函式的執行時間,並收集性能指標,用於分析和優化代碼的效能。