前言
在装饰器中使用其他函数是一种常见的需求,可以帮助你将复杂的功能分解成更小、更易于管理的部分。
使用辅助函数进行日志记录
假设你有一个装饰器,用于在函数调用前后记录日志。你可以将日志记录的功能提取到一个单独的辅助函数中。
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)
使用辅助函数进行输入验证
假设你有一个装饰器,用于验证函数的输入参数。你可以将输入验证的逻辑提取到一个单独的辅助函数中。
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
使用辅助函数进行缓存
假设你有一个装饰器,用于缓存函数的结果。你可以将缓存的逻辑提取到一个单独的辅助函数中。
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)) # 直接从缓存中获取结果
使用辅助函数进行权限验证
假设你有一个装饰器,用于验证用户是否有权限调用某个函数。你可以将权限验证的逻辑提取到一个单独的辅助函数中。
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):
# 假设这里有一个权限检查的逻辑
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
使用辅助函数进行性能测量
假设你有一个装饰器,用于测量函数的执行时间。你可以将性能测量的逻辑提取到一个单独的辅助函数中。
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()
如何在装饰器中使用多个装饰器?
基本的多重装饰器
假设你有两个装饰器 @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}")
带参数的多重装饰器
假设你有一个带参数的装饰器 @repeat,用于多次调用函数,同时还有一个 @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 保留元数据
为了保留被装饰函数的元数据(如名称、文档字符串等),可以使用 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 或者函数的文档字符串
组合多个带参数的装饰器
假设你有两个带参数的装饰器 @repeat 和 @log_level,分别用于多次调用函数和设置日志级别。
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)
总结
通过将装饰器中的复杂逻辑提取到单独的辅助函数中,可以使装饰器更加模块化和易于维护。这些辅助函数可以被多个装饰器复用,从而提高代码的重用性和可读性。
通过组合多个装饰器,可以实现更复杂的功能。多个装饰器的执行顺序是从内到外,因此最靠近函数定义的装饰器会首先被应用。使用 functools.wraps 可以保留被装饰函数的元数据,使代码更加清晰和易读。希望这些示例能帮助你更好地理解如何在装饰器中使用多个装饰器。