在 Python 中,装饰器是非常强大的工具,用于修改或扩展函数的行为。然而,使用装饰器时,我们经常会遇到一个问题:被装饰函数的元数据信息(如名称、文档字符串和参数列表)可能会丢失。这时,functools.wraps
就派上了用场。本文将深入探讨 functools.wraps
的作用,并提供一些实际的应用例子。
functools.wraps 是 Python 标准库中的一个装饰器,用于将原始函数的元数据复制到装饰器函数中。这些元数据包括函数名称(__name__
)、文档字符串(__doc__
)和参数签名(__annotations__
)等。
让我们先来看一个简单的例子,展示 functools.wraps
的基本用法:
import functools
# 不使用 functools.wraps 的装饰器
def my_decorator_without_wraps(func):
def wrapper(*args, **kwargs):
"""Wrapper function docstring."""
print(f'Calling function: {func.__name__}')
return func(*args, **kwargs)
return wrapper
# 使用 functools.wraps 的装饰器
def my_decorator_with_wraps(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
"""Wrapper function docstring."""
print(f'Calling function: {func.__name__}')
return func(*args, **kwargs)
return wrapper
# 被装饰的函数
@my_decorator_without_wraps
def example_function_without_wraps(x, y):
"""Example function docstring."""
return x + y
@my_decorator_with_wraps
def example_function_with_wraps(x, y):
"""Example function docstring."""
return x + y
# 比较输出
print('Without wraps:')
print(example_function_without_wraps.__name__) # 输出: wrapper
print(example_function_without_wraps.__doc__) # 输出: Wrapper function docstring.
print('\nWith wraps:')
print(example_function_with_wraps.__name__) # 输出: example_function_with_wraps
print(example_function_with_wraps.__doc__) # 输出: Example function docstring.
详细解释
my_decorator_without_wraps
和 my_decorator_with_wraps
:my_decorator_without_wraps
接受一个函数 func 作为参数,并返回一个新的函数 wrapper,但没有使用 functools.wraps
。my_decorator_with_wraps
接受一个函数 func 作为参数,并返回一个新的函数 wrapper,使用 functools.wraps(func)
来确保 wrapper 函数具有 func 函数的元数据。@my_decorator_without_wraps
应用于 example_function_without_wraps
,即 example_function_without_wraps
被 my_decorator_without_wraps
装饰器包装。@my_decorator_with_wraps
应用于 example_function_with_wraps
,即 example_function_with_wraps
被 my_decorator_with_wraps
装饰器包装。example_function_without_wraps.__name__
输出被装饰函数的名称,这里输出 wrapper,而不是 example_function_without_wraps
。example_function_without_wraps.__doc__
输出被装饰函数的文档字符串,这里输出 Wrapper function docstring
.,而不是 Example function docstring
.。example_function_with_wraps.__name__
输出被装饰函数的名称,这里输出 example_function_with_wraps
,而不是 wrapper。example_function_with_wraps.__doc__
输出被装饰函数的文档字符串,这里输出 Example function docstring
.,而不是 Wrapper function docstring.。通过这个对比示例,可以清楚地看到 functools.wraps 的作用。它可以保留被装饰函数的元数据,使得装饰器不会意外地修改函数的元信息,从而提高代码的可维护性和可读性。
我们可以使用 functools.wraps 来创建一个计时装饰器,用于测量函数执行的时间。
import time
import functools
def timer(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f'Function {func.__name__} took {end_time - start_time:.4f} seconds')
return result
return wrapper
@timer
def example_function(n):
"""Example function that sleeps for n seconds."""
time.sleep(n)
example_function(2)
print(example_function.__name__) # 输出: example_function
print(example_function.__doc__) # 输出: Example function that sleeps for n seconds.
我们可以创建一个装饰器来检查用户权限,如果
用户没有足够的权限,则抛出异常。
import functools
def requires_permission(permission):
def decorator(func):
@functools.wraps(func)
def wrapper(user, *args, **kwargs):
if user.permission < permission:
raise PermissionError("Insufficient permissions")
return func(user, *args, **kwargs)
return wrapper
return decorator
@requires_permission(2)
def example_function(user):
"""Example function that requires permission level 2."""
return "Access granted"
class User:
def __init__(self, permission):
self.permission = permission
user = User(permission=1)
try:
example_function(user)
except PermissionError as e:
print(e) # 输出: Insufficient permissions
print(example_function.__name__) # 输出: example_function
print(example_function.__doc__) # 输出: Example function that requires permission level 2.
我们可以使用装饰器在函数调用前后记录日志信息
import functools
def logger(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(f'Calling {func.__name__} with args: {args}, kwargs: {kwargs}')
result = func(*args, **kwargs)
print(f'{func.__name__} returned {result}')
return result
return wrapper
@logger
def example_function(a, b):
"""Example function that adds two numbers."""
return a + b
example_function(3, 4)
print(example_function.__name__) # 输出: example_function
print(example_function.__doc__) # 输出: Example function that adds two numbers.
通过使用 functools.wraps,我们可以确保装饰器不会意外地修改被装饰函数的元数据,从而提高代码的可维护性和可读性。无论是简单的计时装饰器、权限检查装饰器,还是日志记录装饰器,functools.wraps 都是保持函数原始信息的重要工具。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有