跳转至

装饰器副作用


from typing import Any


def get_func_info(func):
    def wrap(param: Any) -> None:
        """this is the wrap function"""
        func(param)
    return wrap


@get_func_info
def func(param: str) -> None:
    """打印“世界你好”"""
    print(param)


def main():
    print(f'func name is: {func.__name__}')
    print(f'func docs is: {func.__doc__}')
    print(f'func annotations is: {func.__annotations__}')
    func('Hello World')


if __name__ == '__main__':
    main()

输出:

func name is: wrap
func docs is: this is the wrap function
func annotations is: {'param': typing.Any, 'return': None}
Hello World

解释:

@get_func_info 装饰器等价于 func = get_func_info(func)

所以 func() 实际指向的是 wrap()

因此输出的 wrap 的名称、文档和注解

这就是装饰器的副作用,装饰器不会改变原有函数的功能,但会改变原有函数的一些属性

解决办法就是使用 functools.wraps 装饰 装饰器的内部函数,代码如下:

from functools import wraps
from typing import Any


def get_func_info(func):
    # 注意此处,使用 @wraps(func) 装饰 装饰器的内部函数,抵消装饰器对原函数带来的副作用
    @wraps(func)
    def wrap(param: Any) -> None:
        """this is the wrap function"""
        func(param)
    return wrap


@get_func_info
def func(param: str) -> None:
    """打印“世界你好”"""
    print(param)


def main():
    print(f'func name is: {func.__name__}')
    print(f'func docs is: {func.__doc__}')
    print(f'func annotations is: {func.__annotations__}')
    func('Hello World')


if __name__ == '__main__':
    main()

输出:

func name is: func
func docs is: 打印“世界你好”
func annotations is: {'param': <class 'str'>, 'return': None}
Hello World