简介
Python 装饰器是用于修改和增强函数行为的强大工具,但它们有时会模糊重要的函数元数据。functools.wraps 装饰器为这个挑战提供了一个优雅的解决方案,帮助开发者创建更健壮、更透明的包装函数,同时保留原始函数的基本特性。
Python 装饰器是用于修改和增强函数行为的强大工具,但它们有时会模糊重要的函数元数据。functools.wraps 装饰器为这个挑战提供了一个优雅的解决方案,帮助开发者创建更健壮、更透明的包装函数,同时保留原始函数的基本特性。
Python 中的装饰器是一种强大的方式,用于在不直接更改函数和方法源代码的情况下对其进行修改或增强。它们本质上是将另一个函数作为参数,并返回该函数的修改版本的函数。
def my_decorator(func):
def wrapper():
print("函数调用前的一些操作。")
func()
print("函数调用后的一些操作。")
return wrapper
@my_decorator
def say_hello():
print("你好!")
say_hello()
在 Python 中,函数是一等对象。这意味着它们可以:
装饰器通常使用嵌套函数来包装原始函数:
def outer_function(original_function):
def inner_function():
## 之前的额外行为
result = original_function()
## 之后的额外行为
return result
return inner_function
装饰器类型 | 描述 | 示例用例 |
---|---|---|
函数装饰器 | 修改函数行为 | 日志记录、计时 |
类装饰器 | 修改类行为 | 单例模式 |
方法装饰器 | 修改方法行为 | 认证 |
import time
def timer_decorator(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"函数 {func.__name__} 耗时 {end_time - start_time} 秒")
return result
return wrapper
@timer_decorator
def slow_function():
time.sleep(2)
print("慢速函数完成")
slow_function()
通过理解这些基本概念,开发者可以利用装饰器编写更优雅、高效的 Python 代码。LabEx 建议通过实践这些技术来掌握装饰器的实现。
在创建装饰器时,元数据保留是一个常见问题。如果处理不当,被装饰的函数会失去其原始元数据,如名称、文档字符串和其他属性。
functools.wraps
是一个装饰器,旨在解决创建自定义装饰器时的元数据保留问题。
from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
"""包装函数文档"""
return func(*args, **kwargs)
return wrapper
import functools
def without_wraps(func):
def wrapper(*args, **kwargs):
"""包装函数"""
return func(*args, **kwargs)
return wrapper
def with_wraps(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
"""包装函数"""
return func(*args, **kwargs)
return wrapper
@without_wraps
def original_function():
"""原始函数文档"""
pass
@with_wraps
def wrapped_function():
"""原始函数文档"""
pass
## 元数据比较
print("不使用wraps:")
print(without_wraps.__name__)
print(without_wraps.__doc__)
print("\n使用wraps:")
print(with_wraps.__name__)
print(with_wraps.__doc__)
优点 | 描述 |
---|---|
元数据保留 | 保留原始函数的元数据 |
调试支持 | 改善调试和自省 |
一致的函数表示 | 保持函数的原始标识 |
def decorator1(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print("装饰器1")
return func(*args, **kwargs)
return wrapper
def decorator2(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print("装饰器2")
return func(*args, **kwargs)
return wrapper
@decorator1
@decorator2
def combined_function():
"""带有多个装饰器的函数"""
pass
@functools.wraps
functools.wraps
的性能开销极小,建议在大多数装饰器实现中使用。
LabEx建议在装饰器设计中把使用functools.wraps
作为标准做法,以确保代码简洁、易于维护。
import functools
import logging
def log_function_call(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
logging.info(f"正在调用 {func.__name__}")
try:
result = func(*args, **kwargs)
logging.info(f"{func.__name__} 成功完成")
return result
except Exception as e:
logging.error(f"{func.__name__} 中出现异常: {str(e)}")
raise
return wrapper
@log_function_call
def calculate_total(items):
"""计算商品的总价"""
return sum(item['price'] for item in items)
def require_auth(func):
@functools.wraps(func)
def wrapper(user, *args, **kwargs):
if not user.is_authenticated:
raise PermissionError("用户未认证")
return func(user, *args, **kwargs)
return wrapper
class User:
def __init__(self, username, is_authenticated=False):
self.username = username
self.is_authenticated = is_authenticated
@require_auth
def access_sensitive_data(user, data):
"""访问敏感系统信息"""
return data
import time
import functools
def performance_tracker(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"{func.__name__} 执行耗时 {end_time - start_time:.4f} 秒")
return result
return wrapper
@performance_tracker
def complex_calculation(n):
"""执行复杂的数学计算"""
return sum(i**2 for i in range(n))
import functools
def memoize(func):
cache = {}
@functools.wraps(func)
def wrapper(*args):
if args not in cache:
cache[args] = func(*args)
return cache[args]
return wrapper
@memoize
def fibonacci(n):
"""使用记忆化计算斐波那契数"""
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
场景 | 用例 | 装饰器的好处 |
---|---|---|
认证 | 限制访问 | 集中授权逻辑 |
日志记录 | 跟踪函数调用 | 将日志记录与核心逻辑分离 |
缓存 | 优化重复计算 | 提高性能 |
计时 | 测量执行时间 | 性能监控 |
验证 | 输入/输出检查 | 确保数据完整性 |
functools.wraps
保留元数据LabEx建议理解这些实际应用,以便在Python开发中有效地利用装饰器。
通过理解和应用functools.wraps装饰器,Python开发者可以创建更复杂、更简洁的装饰器实现。这项技术确保被包装的函数保留其原始元数据,使调试、自省和文档记录更加直接,并在复杂的装饰器模式中保持函数级信息的完整性。