Python 如何在裝飾器中使用其他函數(shù)?
前言
在裝飾器中使用其他函數(shù)是一種常見的需求,可以幫助你將復雜的功能分解成更小、更易于管理的部分。
使用輔助函數(shù)進行日志記錄
假設(shè)你有一個裝飾器,用于在函數(shù)調(diào)用前后記錄日志。你可以將日志記錄的功能提取到一個單獨的輔助函數(shù)中。
import logging
# 配置日志記錄
logging.basicConfig(level=logging.INFO)
def log_message(message):
logging.info(message)
def log_function_call(func):
def wrapper(*args, **kwargs):
log_message(f"Calling {func.__name__} with args={args} kwargs={kwargs}")
result = func(*args, **kwargs)
log_message(f"{func.__name__} returned {result}")
return result
return wrapper
@log_function_call
def add(a, b):
return a + b
add(3, 5)
使用輔助函數(shù)進行輸入驗證
假設(shè)你有一個裝飾器,用于驗證函數(shù)的輸入?yún)?shù)。你可以將輸入驗證的邏輯提取到一個單獨的輔助函數(shù)中。
def validate_input(*types):
def decorator(func):
def wrapper(*args, **kwargs):
if len(args) != len(types):
raise TypeError("Number of arguments does not match expected types")
for arg, type_ in zip(args, types):
if not isinstance(arg, type_):
raise TypeError(f"Argument {arg} is not of type {type_}")
return func(*args, **kwargs)
return wrapper
return decorator
def is_positive(number):
if number <= 0:
raise ValueError("Number must be positive")
return number
@validate_input(int, int)
def add(a, b):
return a + b
@validate_input(int)
def square(n):
return n ** 2
add(3, 5) # 正常運行
square(4) # 正常運行
# square(-4) # 拋出 ValueError
使用輔助函數(shù)進行緩存
假設(shè)你有一個裝飾器,用于緩存函數(shù)的結(jié)果。你可以將緩存的邏輯提取到一個單獨的輔助函數(shù)中。
from functools import lru_cache
def cache_results(func):
@lru_cache(maxsize=128)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
def compute_fibonacci(n):
if n <= 1:
return n
return compute_fibonacci(n - 1) + compute_fibonacci(n - 2)
@cache_results
def fibonacci(n):
return compute_fibonacci(n)
print(fibonacci(10)) # 輸出: 55
print(fibonacci(10)) # 直接從緩存中獲取結(jié)果
使用輔助函數(shù)進行權(quán)限驗證
假設(shè)你有一個裝飾器,用于驗證用戶是否有權(quán)限調(diào)用某個函數(shù)。你可以將權(quán)限驗證的邏輯提取到一個單獨的輔助函數(shù)中。
def check_permission(permission):
def decorator(func):
def wrapper(*args, **kwargs):
if not has_permission(permission):
raise PermissionError(f"User does not have permission {permission}")
return func(*args, **kwargs)
return wrapper
return decorator
def has_permission(permission):
# 假設(shè)這里有一個權(quán)限檢查的邏輯
return permission == "admin"
@check_permission("admin")
def admin_action():
print("Performing admin action")
@check_permission("user")
def user_action():
print("Performing user action")
admin_action() # 正常運行
# user_action() # 拋出 PermissionError
使用輔助函數(shù)進行性能測量
假設(shè)你有一個裝飾器,用于測量函數(shù)的執(zhí)行時間。你可以將性能測量的邏輯提取到一個單獨的輔助函數(shù)中。
import time
def measure_time(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"{func.__name__} took {end_time - start_time:.4f} seconds to execute")
return result
return wrapper
def get_current_time():
return time.time()
@measure_time
def slow_function():
time.sleep(2)
print("Slow function completed")
slow_function()
如何在裝飾器中使用多個裝飾器?
基本的多重裝飾器
假設(shè)你有兩個裝飾器 @log_function_call 和 @measure_time,分別用于日志記錄和性能測量。
import logging
import time
# 配置日志記錄
logging.basicConfig(level=logging.INFO)
def log_message(message):
logging.info(message)
def log_function_call(func):
def wrapper(*args, **kwargs):
log_message(f"Calling {func.__name__} with args={args} kwargs={kwargs}")
result = func(*args, **kwargs)
log_message(f"{func.__name__} returned {result}")
return result
return wrapper
def measure_time(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"{func.__name__} took {end_time - start_time:.4f} seconds to execute")
return result
return wrapper
@log_function_call
@measure_time
def add(a, b):
time.sleep(1) # 模擬耗時操作
return a + b
result = add(3, 5)
print(f"Result: {result}")
帶參數(shù)的多重裝飾器
假設(shè)你有一個帶參數(shù)的裝飾器 @repeat,用于多次調(diào)用函數(shù),同時還有一個 @log_function_call 裝飾器。
import logging
# 配置日志記錄
logging.basicConfig(level=logging.INFO)
def log_message(message):
logging.info(message)
def log_function_call(func):
def wrapper(*args, **kwargs):
log_message(f"Calling {func.__name__} with args={args} kwargs={kwargs}")
result = func(*args, **kwargs)
log_message(f"{func.__name__} returned {result}")
return result
return wrapper
def repeat(num_times):
def decorator(func):
def wrapper(*args, **kwargs):
results = []
for _ in range(num_times):
result = func(*args, **kwargs)
results.append(result)
return results
return wrapper
return decorator
@log_function_call
@repeat(3)
def greet(name):
return f"Hello, {name}!"
results = greet("Alice")
for result in results:
print(result)
使用 functools.wraps 保留元數(shù)據(jù)
為了保留被裝飾函數(shù)的元數(shù)據(jù)(如名稱、文檔字符串等),可以使用 functools.wraps。
import logging
import time
import functools
# 配置日志記錄
logging.basicConfig(level=logging.INFO)
def log_message(message):
logging.info(message)
def log_function_call(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
log_message(f"Calling {func.__name__} with args={args} kwargs={kwargs}")
result = func(*args, **kwargs)
log_message(f"{func.__name__} returned {result}")
return result
return wrapper
def measure_time(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"{func.__name__} took {end_time - start_time:.4f} seconds to execute")
return result
return wrapper
@log_function_call
@measure_time
def add(a, b):
time.sleep(1) # 模擬耗時操作
return a + b
result = add(3, 5)
print(f"Result: {result}")
print(add.__name__) # 輸出: add
print(add.__doc__) # 輸出: None 或者函數(shù)的文檔字符串
組合多個帶參數(shù)的裝飾器
假設(shè)你有兩個帶參數(shù)的裝飾器 @repeat 和 @log_level,分別用于多次調(diào)用函數(shù)和設(shè)置日志級別。
import logging
import functools
# 配置日志記錄
logging.basicConfig(level=logging.DEBUG)
def set_log_level(level):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
logger = logging.getLogger(func.__name__)
logger.setLevel(level)
result = func(*args, **kwargs)
return result
return wrapper
return decorator
def repeat(num_times):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
results = []
for _ in range(num_times):
result = func(*args, **kwargs)
results.append(result)
return results
return wrapper
return decorator
@set_log_level(logging.INFO)
@repeat(3)
def greet(name):
logger = logging.getLogger(greet.__name__)
logger.info(f"Greeting {name}")
return f"Hello, {name}!"
results = greet("Alice")
for result in results:
print(result)
總結(jié)
通過將裝飾器中的復雜邏輯提取到單獨的輔助函數(shù)中,可以使裝飾器更加模塊化和易于維護。這些輔助函數(shù)可以被多個裝飾器復用,從而提高代碼的重用性和可讀性。
通過組合多個裝飾器,可以實現(xiàn)更復雜的功能。多個裝飾器的執(zhí)行順序是從內(nèi)到外,因此最靠近函數(shù)定義的裝飾器會首先被應用。使用 functools.wraps 可以保留被裝飾函數(shù)的元數(shù)據(jù),使代碼更加清晰和易讀。希望這些示例能幫助你更好地理解如何在裝飾器中使用多個裝飾器。