裝飾器通過捕獲異常并分別處理不同類型的錯誤來提升代碼的健壯性和可維護性。1. 裝飾器本質上是語法糖,用于包裹目標函數并在其外部統一處理異常;2. 可以針對不同異常類型編寫特定邏輯,例如對網絡超時進行重試,而對參數錯誤直接拋出異常;3. 為避免裝飾器嵌套導致性能下降,應簡化內部邏輯、使用緩存或合并依賴裝飾器;4. 裝飾器還可用于權限校驗、日志記錄、性能監控、數據驗證、緩存和事務管理等場景,從而擴展函數行為而不修改其本身。
裝飾器本質上是語法糖,利用它可以優雅地統一處理函數異常,順便還能加上重試機制。這東西用好了,代碼瞬間清爽不少。
解決方案
核心思路是,裝飾器包裹目標函數,在裝飾器內部捕獲異常,然后根據需要進行處理,比如記錄日志、重試、返回默認值等等。
import functools import time import logging logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') def retry(max_retries=3, delay=1, logger=None): """ 一個通用的重試裝飾器,可以自定義重試次數、延遲時間和日志記錄器。 """ def decorator_retry(func): @functools.wraps(func) def wrapper_retry(*args, **kwargs): retries = 0 while retries < max_retries: try: return func(*args, **kwargs) except Exception as e: retries += 1 log_message = f"Function {func.__name__} failed with error: {e}, retrying ({retries}/{max_retries})..." if logger: logger.warning(log_message) else: logging.warning(log_message) time.sleep(delay) # 如果所有重試都失敗了,可以選擇拋出異?;蚍祷啬J值 raise Exception(f"Function {func.__name__} failed after {max_retries} retries.") return wrapper_retry return decorator_retry def exception_handler(default_value=None, logger=None): """ 一個簡單的異常處理裝飾器,可以記錄日志并返回默認值。 """ def decorator_exception_handler(func): @functools.wraps(func) def wrapper_exception_handler(*args, **kwargs): try: return func(*args, **kwargs) except Exception as e: log_message = f"Function {func.__name__} raised an exception: {e}" if logger: logger.error(log_message) else: logging.error(log_message) return default_value # 返回默認值 return wrapper_exception_handler return decorator_exception_handler @retry(max_retries=3, delay=2, logger=logging) # 使用retry裝飾器,最多重試3次,每次間隔2秒 @exception_handler(default_value="Error", logger=logging) # 使用exception_handler裝飾器,發生異常時返回"Error" def might_fail(attempt): """ 一個可能失敗的函數,用于演示。 """ print(f"Attempt {attempt}: Trying to execute might_fail...") if attempt < 2: raise ValueError(f"Simulated failure on attempt {attempt}") return f"Success on attempt {attempt}" # 示例用法 if __name__ == "__main__": for i in range(3): try: result = might_fail(i) print(f"Result: {result}") break except Exception as e: print(f"Main block caught an exception: {e}")
裝飾器如何處理不同類型的異常?
裝飾器可以針對不同類型的異常進行不同的處理。例如,可以針對網絡請求超時進行重試,而對于參數錯誤則直接拋出異常。
import functools import time import logging import requests logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') def specific_exception_handler(func): """ 針對特定異常類型的處理裝飾器。 """ @functools.wraps(func) def wrapper(*args, **kwargs): try: return func(*args, **kwargs) except requests.exceptions.RequestException as e: logging.error(f"Network error in {func.__name__}: {e}") # 可以選擇重試網絡請求,或者返回一個特定的錯誤值 return None except ValueError as e: logging.error(f"Value error in {func.__name__}: {e}") raise # 重新拋出ValueError,因為這通常是輸入錯誤,不應該被忽略 except Exception as e: logging.error(f"Unexpected error in {func.__name__}: {e}") return None # 對于其他未知的異常,返回None return wrapper @specific_exception_handler def fetch_data(url): """ 模擬獲取數據的函數,可能會拋出網絡請求異?;騐alueError。 """ print(f"Fetching data from {url}...") response = requests.get(url, timeout=5) response.raise_for_status() # 如果響應狀態碼不是200,則拋出HTTPError return response.json() if __name__ == "__main__": try: data = fetch_data("https://api.example.com/data") # 假設這是一個有效的API endpoint print(f"Data: {data}") except Exception as e: print(f"Main block caught an exception: {e}") try: data = fetch_data("invalid_url") # 模擬一個無效的URL,會拋出requests.exceptions.RequestException print(f"Data: {data}") except Exception as e: print(f"Main block caught an exception: {e}")
如何避免裝飾器過度嵌套導致性能下降?
裝飾器嵌套確實可能導致性能問題,尤其是當裝飾器內部邏輯比較復雜時。解決這個問題,一方面要盡量簡化裝飾器內部的邏輯,另一方面可以考慮使用functools.lru_cache來緩存裝飾器的結果,避免重復計算。此外,如果裝飾器之間存在依賴關系,可以考慮將它們合并成一個裝飾器。
除了重試和異常處理,裝飾器還能用于哪些場景?
除了重試和異常處理,裝飾器還能用于很多其他的場景,比如:
- 權限校驗: 檢查用戶是否有權限訪問某個函數。
- 日志記錄: 記錄函數的調用信息,包括參數和返回值。
- 性能監控: 統計函數的執行時間。
- 數據驗證: 驗證函數的輸入參數是否符合要求。
- 緩存: 緩存函數的返回值,避免重復計算。
- 事務管理: 在函數執行前后開啟和關閉數據庫事務。
? 版權聲明
文章版權歸作者所有,未經允許請勿轉載。
THE END