Python 装饰器如何影响函数签名?

来源:网站主作者:狼行天下头衔:草根站长
导读:本期聚焦于小伙伴创作的《Python 装饰器如何影响函数签名?》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《Python 装饰器如何影响函数签名?》有用,将其分享出去将是对创作者最好的鼓励。

Python装饰器是影响函数行为的常用工具,它可以在不修改原函数代码的前提下,为函数添加额外的功能。但装饰器的使用往往会改变被装饰函数的签名信息,导致通过inspect模块获取的参数、注解等内容和原函数不一致,影响代码的可用性和可维护性。

Python 装饰器如何影响函数签名?

函数签名的基本概念

函数签名是描述函数特征的信息集合,主要包括函数的参数列表、参数默认值、参数注解、返回值注解等内容。在Python中,我们可以通过inspect模块的signature函数获取函数的签名信息,示例如下:

import inspect

def original_func(name: str, age: int = 18) -> str:
    return f"name:{name}, age:{age}"

# 获取原函数签名
sig = inspect.signature(original_func)
print(sig)  # 输出 (name: str, age: int = 18) -> str
print(sig.parameters)  # 输出 OrderedDict([('name', <Parameter "name: str">), ('age', <Parameter "age: int = 18">)])

普通装饰器对函数签名的影响

最常见的装饰器写法是定义一个外层函数接收原函数,再定义内层包装函数执行额外逻辑后调用原函数,最后返回包装函数。这种写法会直接替换原函数的引用,导致签名信息变成包装函数的签名。

我们看一个基础装饰器的示例:

import inspect

def simple_decorator(func):
    def wrapper(*args, **kwargs):
        print("装饰器额外逻辑执行")
        return func(*args, **kwargs)
    return wrapper

@simple_decorator
def decorated_func(name: str, age: int = 18) -> str:
    return f"name:{name}, age:{age}"

# 获取被装饰函数的签名
sig = inspect.signature(decorated_func)
print(sig)  # 输出 (*args, **kwargs)
print(sig.parameters)  # 输出 OrderedDict([('args', <Parameter "*args">), ('kwargs', <Parameter "**kwargs">)])

可以看到,被装饰后的decorated_func的签名已经变成了包装函数wrapper的签名,原函数的参数定义、注解等所有签名信息都丢失了。这是因为装饰器返回的是wrapper函数对象,decorated_func的引用已经指向了wrapper,自然获取到的就是wrapper的签名。

带参数的装饰器对函数签名的影响

带参数的装饰器会比普通装饰器多一层函数嵌套,用来接收装饰器参数,最终返回的还是包装函数,因此对函数签名的影响和普通装饰器一致,同样会丢失原函数的签名信息。

示例如下:

import inspect

def param_decorator(prefix=""):
    def decorator(func):
        def wrapper(*args, **kwargs):
            print(f"前缀:{prefix}")
            return func(*args, **kwargs)
        return wrapper
    return decorator

@param_decorator(prefix="测试")
def param_decorated_func(name: str) -> str:
    return f"name:{name}"

sig = inspect.signature(param_decorated_func)
print(sig)  # 输出 (*args, **kwargs)

如何保留原函数的函数签名

Python标准库的functools模块提供了wraps装饰器,它可以把原函数的签名、名称、文档字符串等属性复制到包装函数上,从而避免装饰器对函数签名的影响。

使用wraps的示例如下:

import inspect
from functools import wraps

def wraps_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("装饰器额外逻辑执行")
        return func(*args, **kwargs)
    return wrapper

@wraps_decorator
def wraps_decorated_func(name: str, age: int = 18) -> str:
    return f"name:{name}, age:{age}"

sig = inspect.signature(wraps_decorated_func)
print(sig)  # 输出 (name: str, age: int = 18) -> str
print(wraps_decorated_func.__name__)  # 输出 wraps_decorated_func,而不是wrapper

wraps的实现原理是读取原函数的__wrapped__属性,同时复制原函数的__name____doc____annotations__等属性到包装函数上,因此inspect.signature在获取签名时,会追溯到__wrapped__指向的原函数,从而返回正确的签名信息。

不同场景下的签名影响对比

我们可以通过表格对比不同装饰器写法对函数签名的影响:

装饰器类型是否保留原函数签名签名内容
无装饰器原函数的参数、注解、返回值信息
普通装饰器(无wraps)包装函数的*args, **kwargs参数
带参数装饰器(无wraps)最内层包装函数的*args, **kwargs参数
使用functools.wraps的装饰器原函数的参数、注解、返回值信息

注意事项

如果包装函数本身定义了明确的参数,那么即使不使用wraps,签名也会是包装函数的参数,但此时参数信息和原函数可能不一致,仍然不符合预期。因此只要编写装饰器,都建议加上@wraps(func)的写法,保证函数签名的正确性。

另外,如果装饰器需要修改函数的参数,比如新增参数或者删除参数,那么签名会自然发生变化,这种场景下属于合理的签名修改,不需要强制保留原签名,只需要保证新的签名符合装饰器的参数设计即可。

Python装饰器函数签名functoolsinspect修改时间:2026-06-21 09:15:28

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。