前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python 新手突破瓶颈指南:functools.wraps 元数据复制

Python 新手突破瓶颈指南:functools.wraps 元数据复制

作者头像
MegaQi
发布2024-08-09 14:19:58
1140
发布2024-08-09 14:19:58
举报
文章被收录于专栏:非典型性程序员

在 Python 中,装饰器是非常强大的工具,用于修改或扩展函数的行为。然而,使用装饰器时,我们经常会遇到一个问题:被装饰函数的元数据信息(如名称、文档字符串和参数列表)可能会丢失。这时,functools.wraps 就派上了用场。本文将深入探讨 functools.wraps 的作用,并提供一些实际的应用例子。

functools.wraps

functools.wraps 是 Python 标准库中的一个装饰器,用于将原始函数的元数据复制到装饰器函数中。这些元数据包括函数名称(__name__)、文档字符串(__doc__)和参数签名(__annotations__)等。

基本用法

让我们先来看一个简单的例子,展示 functools.wraps 的基本用法:

代码语言:javascript
复制
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.


详细解释

  1. 定义装饰器函数 my_decorator_without_wrapsmy_decorator_with_wraps
    • my_decorator_without_wraps 接受一个函数 func 作为参数,并返回一个新的函数 wrapper,但没有使用 functools.wraps
    • my_decorator_with_wraps 接受一个函数 func 作为参数,并返回一个新的函数 wrapper,使用 functools.wraps(func) 来确保 wrapper 函数具有 func 函数的元数据。
  2. 使用装饰器:
    • @my_decorator_without_wraps 应用于 example_function_without_wraps,即 example_function_without_wrapsmy_decorator_without_wraps 装饰器包装。
    • @my_decorator_with_wraps 应用于 example_function_with_wraps,即 example_function_with_wrapsmy_decorator_with_wraps 装饰器包装。
  3. 查看被装饰函数的信息:
    • 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 的作用。它可以保留被装饰函数的元数据,使得装饰器不会意外地修改函数的元信息,从而提高代码的可维护性和可读性。

实际应用场景

1. 计时装饰器

我们可以使用 functools.wraps 来创建一个计时装饰器,用于测量函数执行的时间。

代码语言:javascript
复制
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.

2. 权限检查装饰器

我们可以创建一个装饰器来检查用户权限,如果

用户没有足够的权限,则抛出异常。

代码语言:javascript
复制
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.

3. 日志记录装饰器

我们可以使用装饰器在函数调用前后记录日志信息

代码语言:javascript
复制
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 都是保持函数原始信息的重要工具。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-08-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 非典型性程序员 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • functools.wraps
    • 基本用法
      • 实际应用场景
        • 1. 计时装饰器
        • 2. 权限检查装饰器
        • 3. 日志记录装饰器
      • 总结
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档