python中functools.wraps的作用

使用functools.wraps
本文介绍了Python中functools.wraps的用法,通过对比带与不带此装饰器的函数名变化,展示了它如何保持被装饰函数的元信息。

functools.wraps 的作用是将原函数对象的指定属性复制给包装函数对象, 默认有 modulenamedoc,或者通过参数选择。代码如下:

import logging.handlers
import time
import copy
import sys
import os
import logging
import functools


def create_logger():
    logfile = sys.path[0] + os.sep + 'notedb.log'
    handler = logging.handlers.RotatingFileHandler(logfile, mode='a')
    fmt = '%(asctime)s - %(filename)s:%(lineno)s - %(name)s - %(message)s'
    fmtstr = logging.Formatter(fmt)
    handler.setFormatter(fmtstr)
    logger = logging.getLogger('Message')
    logger.addHandler(handler)
    return logger


def logging_warpper(logger):
    def warpper_(func):
        @functools.wraps(func)
        def warpper(*args, **kwargs):
            try:
                print("begin the func %s" % (time.time()))
                print("The func address is %s " % (func.__name__))
                func(*args, **kwargs)
                print("end the func %s" % (time.time()))
            except:
                err = "There was an exception in  "
                err += func.__name__
                logger.exception(err)

                raise
        return warpper
    return warpper_


@logging_warpper(create_logger())
def addfunc(list_seq, args):
    seq = copy.deepcopy(list_seq)
    print(sum(x * x for x in seq))
    for key, value in args.items():
        print(key, value, end=' ')
    print('\n')


dict = {1: 'yangyc', 2: 'chen'}
list_use = list(range(10))
addfunc(list_use, dict)
print("The finally func name is :", addfunc.__name__)

输出结果:

begin the func 1508897893.4947305
The func address is addfunc 
285
1 yangyc 2 chen 

end the func 1508897893.4947305
The finally func name is : addfunc

func.name 为原函数addfunc的name

如果不添加functools.wraps修饰器,代码如下:

import logging.handlers
import time
import copy
import sys
import os
import logging
import functools


def create_logger():
    logfile = sys.path[0] + os.sep + 'notedb.log'
    handler = logging.handlers.RotatingFileHandler(logfile, mode='a')
    fmt = '%(asctime)s - %(filename)s:%(lineno)s - %(name)s - %(message)s'
    fmtstr = logging.Formatter(fmt)
    handler.setFormatter(fmtstr)
    logger = logging.getLogger('Message')
    logger.addHandler(handler)
    return logger


def logging_warpper(logger):
    def warpper_(func):
        def warpper(*args, **kwargs):
            try:
                print("begin the func %s" % (time.time()))
                print("The func address is %s " % (func.__name__))
                func(*args, **kwargs)
                print("end the func %s" % (time.time()))
            except:
                err = "There was an exception in  "
                err += func.__name__
                logger.exception(err)

                raise
        return warpper
    return warpper_


@logging_warpper(create_logger())
def addfunc(list_seq, args):
    seq = copy.deepcopy(list_seq)
    print(sum(x * x for x in seq))
    for key, value in args.items():
        print(key, value, end=' ')
    print('\n')


dict = {1: 'yangyc', 2: 'chen'}
list_use = list(range(10))
addfunc(list_use, dict)
print("The finally func name is :", addfunc.__name__)

输出结果如下:

begin the func 1508897997.7467523
The func address is addfunc 
285
1 yangyc 2 chen 

end the func 1508897997.7467523
The finally func name is : warpper

最终的func.name 为warpper

Python 中的 `functools.wraps` 是一个非常有用的装饰器工具,主要用于保留被装饰函数的元数据(metadata),例如函数名、文档字符串、参数列表等。在使用装饰器对函数进行包装时,如果不使用 `functools.wraps`,那么被装饰的函数的一些属性会被覆盖为装饰器内部函数的属性,这会导致调试困难和文档生成问题。 ### 作用 1. **保留原始函数的元数据** 使用 `functools.wraps` 可以确保装饰器不会影响被装饰函数的名称、文档字符串等信息[^2]。 2. **简化 `update_wrapper` 的使用** `wraps` 是 `functools.update_wrapper` 的一个便捷封装,避免了手动调用 `update_wrapper` 来更新装饰器包装后的函数属性[^3]。 3. **提高代码可读性和可维护性** 在调试或使用自动化工具(如 Sphinx 文档生成)时,保留原始函数的信息有助于理解函数的行为和用途[^1]。 --- ### 示例 #### 基本使用方式 ```python from functools import wraps def my_decorator(func): @wraps(func) def wrapper(*args, **kwargs): print("Before function call") result = func(*args, **kwargs) print("After function call") return result return wrapper @my_decorator def say_hello(name): """Greet the user with a message.""" print(f"Hello, {name}!") # 调用函数 say_hello("Alice") # 查看函数名和文档字符串 print(say_hello.__name__) # 输出: say_hello print(say_hello.__doc__) # 输出: Greet the user with a message. ``` 在这个例子中,`@wraps(func)` 确保了 `say_hello` 函数的 `__name__` 和 `__doc__` 属性没有被 `wrapper` 函数覆盖。 #### 不使用 `wraps` 的后果 ```python def my_decorator(func): def wrapper(*args, **kwargs): print("Before function call") result = func(*args, **kwargs) print("After function call") return result return wrapper @my_decorator def say_hello(name): """Greet the user with a message.""" print(f"Hello, {name}!") # 查看函数名和文档字符串 print(say_hello.__name__) # 输出: wrapper print(say_hello.__doc__) # 输出: None ``` 此时,由于没有使用 `@wraps(func)`,`say_hello` 的元数据被 `wrapper` 替代,导致调试和文档生成变得困难。 --- #### 内部机制:与 `update_wrapper` 的关系 `functools.wraps` 实际上是对 `functools.update_wrapper` 的封装。其等效实现如下: ```python from functools import update_wrapper def wraps(wrapped): def decorator(wrapper): update_wrapper(wrapper, wrapped) return wrapper return decorator ``` 它通过调用 `update_wrapper(wrapper, wrapped)` 将 `wrapped` 函数的元数据复制到 `wrapper` 上,从而保留原始函数的信息。 --- ### 最佳实践 - 每次编写装饰器时都应使用 `@wraps(func)`,以确保不丢失原始函数的元数据。 - 在开发库或框架时尤其重要,因为这些信息对于用户理解和使用 API 至关重要。 - 避免手动更新 `__name__` 或 `__doc__`,优先使用 `wraps` 来自动处理。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值