從入門到封裝通用詳解Python裝飾器實戰(zhàn)指南
一、裝飾器初體驗:給函數(shù)穿"外套"
想象你有一件普通白T恤(原始函數(shù)),現(xiàn)在想給它加上圖案(額外功能)卻不改變衣服本身。Python裝飾器就像這件"魔法外套",能動態(tài)為函數(shù)添加功能而不修改原代碼。
1.1 最簡單的裝飾器
def simple_decorator(func):
def wrapper():
print("外套前襟:添加日志")
func()
print("外套后背:記錄耗時")
return wrapper
@simple_decorator
def say_hello():
print("Hello World!")
say_hello()
輸出結(jié)果:
外套前襟:添加日志
Hello World!
外套后背:記錄耗時
這個例子展示了裝飾器的核心機制:@simple_decorator相當(dāng)于say_hello = simple_decorator(say_hello),將原函數(shù)包裹在wrapper中。
1.2 處理帶參數(shù)的函數(shù)
當(dāng)被裝飾函數(shù)需要參數(shù)時,用*args和**kwargs實現(xiàn)萬能適配:
def param_decorator(func):
def wrapper(*args, **kwargs):
print(f"準(zhǔn)備調(diào)用{func.__name__},參數(shù):{args}, {kwargs}")
return func(*args, **kwargs)
return wrapper
@param_decorator
def add(a, b, c=0):
return a + b + c
print(add(1, 2, c=3)) # 輸出:準(zhǔn)備調(diào)用add,參數(shù):(1, 2), {'c': 3} \n 6
二、裝飾器進(jìn)階:解決三大痛點
2.1 保留函數(shù)元信息
直接使用裝飾器會丟失原函數(shù)的__name__、__doc__等信息,用functools.wraps修復(fù):
from functools import wraps
def safe_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
"""這是包裝函數(shù)的文檔"""
print("安全檢查通過")
return func(*args, **kwargs)
return wrapper
@safe_decorator
def calculate(x):
"""計算平方"""
return x ** 2
print(calculate.__name__) # 輸出:calculate(而非wrapper)
print(calculate.__doc__) # 輸出:計算平方(而非包裝函數(shù)的文檔)
2.2 帶參數(shù)的裝飾器
當(dāng)需要為不同函數(shù)配置不同參數(shù)時(如日志級別、緩存時間),使用三層嵌套結(jié)構(gòu):
def configurable_decorator(level="INFO"):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"[{level}] 調(diào)用函數(shù): {func.__name__}")
return func(*args, **kwargs)
return wrapper
return decorator
@configurable_decorator(level="DEBUG")
def fetch_data():
return "數(shù)據(jù)獲取成功"
fetch_data() # 輸出:[DEBUG] 調(diào)用函數(shù): fetch_data
2.3 多個裝飾器疊加
裝飾器執(zhí)行順序遵循"從下往上裝飾,從上往下執(zhí)行"原則:
def decor1(func):
def wrapper():
print("裝飾器1前")
func()
print("裝飾器1后")
return wrapper
def decor2(func):
def wrapper():
print("裝飾器2前")
func()
print("裝飾器2后")
return wrapper
@decor1
@decor2
def target():
print("目標(biāo)函數(shù)執(zhí)行")
target()
"""
輸出:
裝飾器1前
裝飾器2前
目標(biāo)函數(shù)執(zhí)行
裝飾器2后
裝飾器1后
"""
三、實戰(zhàn)場景:打造企業(yè)級工具庫
3.1 性能監(jiān)控裝飾器
自動統(tǒng)計函數(shù)執(zhí)行時間并生成可視化報告:
import time
from functools import wraps
def performance_monitor(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
duration = time.perf_counter() - start
# 實際項目中可集成Prometheus等監(jiān)控系統(tǒng)
print(f"?? {func.__name__} 執(zhí)行耗時: {duration:.4f}秒")
return result
return wrapper
@performance_monitor
def complex_calculation(n):
return sum(i*i for i in range(n))
complex_calculation(1000000) # 輸出:?? complex_calculation 執(zhí)行耗時: 0.1234秒
3.2 緩存裝飾器(Memoization)
對重復(fù)計算結(jié)果進(jìn)行緩存,特別適合遞歸函數(shù):
def cache_decorator(func):
cache = {}
@wraps(func)
def wrapper(n):
if n not in cache:
cache[n] = func(n)
print(f"?? 緩存命中缺失,計算并存儲結(jié)果 for {n}")
else:
print(f"?? 緩存命中 for {n}")
return cache[n]
return wrapper
@cache_decorator
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(10)) # 第二次調(diào)用fibonacci(5)等會直接從緩存讀取
3.3 權(quán)限驗證裝飾器
在Web開發(fā)中驗證用戶權(quán)限的經(jīng)典實現(xiàn):
def role_required(required_role):
def decorator(func):
@wraps(func)
def wrapper(user, *args, **kwargs):
if user.get("role") != required_role:
raise PermissionError(f"需要{required_role}權(quán)限")
return func(user, *args, **kwargs)
return wrapper
return decorator
# 測試用例
admin = {"role": "admin", "name": "Alice"}
guest = {"role": "guest", "name": "Bob"}
@role_required("admin")
def delete_user(user, user_id):
print(f"{user['name']} 刪除了用戶 {user_id}")
try:
delete_user(guest, 123) # 拋出PermissionError
except PermissionError as e:
print(e) # 輸出:需要admin權(quán)限
四、高級技巧:裝飾器工廠與類裝飾器
4.1 動態(tài)配置的裝飾器工廠
根據(jù)運行時參數(shù)生成不同行為的裝飾器:
def retry_decorator(max_attempts=3, delay=1):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(max_attempts):
try:
return func(*args, **kwargs)
except Exception as e:
if attempt == max_attempts - 1:
raise
print(f"?? 嘗試 {attempt+1} 失敗,{delay}秒后重試...")
time.sleep(delay)
return wrapper
return decorator
@retry_decorator(max_attempts=5, delay=2)
def unstable_api_call():
import random
if random.random() < 0.7:
raise ConnectionError("模擬網(wǎng)絡(luò)故障")
return "成功獲取數(shù)據(jù)"
print(unstable_api_call()) # 可能需要多次重試
4.2 類裝飾器實現(xiàn)
當(dāng)需要維護(hù)狀態(tài)時,類裝飾器比函數(shù)更合適:
class CallCounter:
def __init__(self, func):
self.func = func
self.count = 0
def __call__(self, *args, **kwargs):
self.count += 1
print(f"?? 函數(shù) {self.func.__name__} 已被調(diào)用 {self.count} 次")
return self.func(*args, **kwargs)
@CallCounter
def greet(name):
print(f"Hello, {name}!")
greet("Alice") # 輸出:?? 函數(shù) greet 已被調(diào)用 1次 \n Hello, Alice!
greet("Bob") # 輸出:?? 函數(shù) greet 已被調(diào)用 2次 \n Hello, Bob!
五、企業(yè)級封裝:通用工具庫設(shè)計
將常用裝飾器封裝成可配置的Python包:
my_decorators/
├── __init__.py
├── core.py # 基礎(chǔ)裝飾器實現(xiàn)
├── config.py # 默認(rèn)配置
└── utils.py # 輔助工具
core.py示例:
from functools import wraps
import time
from .config import DEFAULT_CACHE_SIZE
def timing(func):
"""基礎(chǔ)性能監(jiān)控裝飾器"""
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
print(f"?? {func.__name__} executed in {time.time()-start:.4f}s")
return result
return wrapper
def cached(max_size=DEFAULT_CACHE_SIZE):
"""可配置大小的緩存裝飾器"""
cache = {}
def decorator(func):
@wraps(func)
def wrapper(*args):
if args in cache:
return cache[args]
result = func(*args)
if len(cache) >= max_size:
cache.popitem() # 簡單LRU實現(xiàn)
cache[args] = result
return result
return wrapper
return decorator
使用示例:
from my_decorators.core import timing, cached
@timing
@cached(max_size=100)
def expensive_computation(x):
return sum(i*i for i in range(x))
print(expensive_computation(10000)) # 首次計算較慢
print(expensive_computation(10000)) # 第二次直接從緩存讀取
六、最佳實踐與避坑指南
性能考量:裝飾器中的操作會影響所有被裝飾函數(shù),避免在wrapper中做耗時操作
異常處理:確保裝飾器正確傳播異常,避免靜默失敗
文檔規(guī)范:為裝飾器編寫清晰的docstring,說明其行為和參數(shù)
測試覆蓋:特別測試裝飾器疊加、參數(shù)傳遞等邊界情況
類型提示:使用Python 3.5+的類型注解提升可維護(hù)性:
from typing import Callable, Any, TypeVar
F = TypeVar('F', bound=Callable[..., Any])
def type_safe_decorator(func: F) -> F:
@wraps(func)
def wrapper(*args, **kwargs) -> Any:
# 類型安全的實現(xiàn)
return func(*args, **kwargs)
return wrapper # type: ignore[return-value]
七、總結(jié)與展望
裝飾器作為Python最強大的特性之一,其應(yīng)用場景遠(yuǎn)不止于此。隨著異步編程的普及,async裝飾器、基于描述符的更復(fù)雜裝飾器等高級用法正在涌現(xiàn)。掌握裝飾器的核心思想——"在不修改原函數(shù)的情況下擴展功能",將使你的代碼更加優(yōu)雅、可維護(hù)且易于擴展。
建議從簡單場景開始實踐,逐步嘗試封裝自己的裝飾器工具庫。記?。汉玫难b飾器應(yīng)該像空氣一樣存在——使用時感受不到它的存在,但離開它代碼將變得笨重不堪。
到此這篇關(guān)于從入門到封裝通用詳解Python裝飾器實戰(zhàn)指南的文章就介紹到這了,更多相關(guān)Python裝飾器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Jupyter notebook命令和編輯模式常用快捷鍵匯總
這篇文章主要介紹了Jupyter notebook命令和編輯模式常用快捷鍵匯總,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-11-11
使用Dajngo 通過代碼添加xadmin用戶和權(quán)限(組)
這篇文章主要介紹了使用Dajngo 通過代碼添加xadmin用戶和權(quán)限(組),具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-07-07
在Django下創(chuàng)建項目以及設(shè)置settings.py教程
今天小編就為大家分享一篇在Django下創(chuàng)建項目以及設(shè)置settings.py教程,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-12-12
Python實現(xiàn)查找數(shù)據(jù)庫最接近的數(shù)據(jù)
這篇文章主要介紹了Python實現(xiàn)查找數(shù)據(jù)庫最接近的數(shù)據(jù),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-06-06
python實現(xiàn)將一個數(shù)組逆序輸出的方法
今天小編就為大家分享一篇python實現(xiàn)將一個數(shù)組逆序輸出的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-06-06
Python 非極大值抑制(NMS)的四種實現(xiàn)詳解
本文主要介紹了非極大值抑制(Non-Maximum Suppression,NMS)的四種實現(xiàn)方式,不同方法對NMS速度的影響各不相同,感興趣的小伙伴可以了解一下2021-11-11
python基礎(chǔ)之編碼規(guī)范總結(jié)
今天帶大家來學(xué)習(xí)python基礎(chǔ)知識,文中對python編碼規(guī)范作了詳細(xì)的介紹,對正在學(xué)習(xí)python的小伙伴們很有幫助,需要的朋友可以參考下2021-05-05
使用bandit對目標(biāo)python代碼進(jìn)行安全函數(shù)掃描的案例分析
這篇文章主要介紹了使用bandit對目標(biāo)python代碼進(jìn)行安全函數(shù)掃描,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-01-01
Python利用Spire.Doc for Python高效實現(xiàn)Word轉(zhuǎn)PDF
在工作中,經(jīng)常會遇到需要把 Word 文檔轉(zhuǎn)換成 PDF 的情況,本文將介紹如何借助 Spire.Doc for Python庫,實現(xiàn) doc 轉(zhuǎn) pdf、docx 轉(zhuǎn) pdf,需要的可以了解下2025-09-09

