inspect.signature()返回Signature实例,含parameters(OrderedDict子类)、return_annotation等字段;需通过.parameters访问Parameter对象获取参数名、默认值、类型注解等,不可直接当字典使用。

inspect.signature() 返回的是什么类型
inspect.signature() 返回一个 Signature 实例,不是字典也不是字符串,而是一个包含 parameters(有序映射)、return_annotation 和 __str__ 方法的专用对象。直接 print(sig) 看到的是人类可读格式,但真要取参数名或默认值,得访问 sig.parameters —— 它是 OrderedDict 子类,键是参数名,值是 Parameter 对象。
常见错误:把 sig 当成字典遍历,或试图用 sig['arg'] 取值。正确做法是:
import inspect
def foo(a, b: int = 10) -> str:
pass
<p>sig = inspect.signature(foo)
for name, param in sig.parameters.items():
print(f"{name}: {param.default if param.default != inspect.Parameter.empty else 'no default'}")</p><h1>输出:</h1><h1>a: no default</h1><h1>b: 10如何安全处理缺失注解和默认值
函数参数可能没写类型注解、没设默认值,甚至用了 *args 或 **kwargs。Parameter 对象的 annotation 和 default 字段会分别等于 inspect.Parameter.empty,不能直接参与类型检查或 JSON 序列化。
- 判断是否有类型注解:
param.annotation is not inspect.Parameter.empty - 判断是否有默认值:
param.default is not inspect.Parameter.empty -
param.kind是关键:值为Parameter.POSITIONAL_ONLY、Parameter.KEYWORD_ONLY等,决定调用时能否传关键字参数 -
*args对应kind == Parameter.VAR_POSITIONAL,**kwargs对应Parameter.VAR_KEYWORD
inspect.signature() 在装饰器里为什么常出错
装饰器(尤其是用 @functools.wraps 包装过的)会让 inspect.signature() 返回被包装函数的签名,但若装饰器本身改变了参数逻辑(比如注入额外参数),或用了闭包/lambda,就可能报 ValueError: callable <lambda> is not supported</lambda> 或返回不一致的签名。
立即学习“Python免费学习笔记(深入)”;
原因:某些装饰器没正确设置 __signature__ 属性,或目标对象不是标准函数(如类的 __call__ 方法、C 扩展函数)。解决方法:
- 优先用
functools.wraps(func),它会复制__signature__ - 手动修复签名:
inspect.signature(func).replace(parameters=[...]) - 对不可信对象加 try/except:
try: sig = inspect.signature(obj) except (ValueError, TypeError): sig = inspect.Signature([])
Python 2 和 typing 模块对 signature 解析的影响
inspect.signature() 是 Python 3.5+ 引入的,Python 2 中不存在;即使在 Python 3 中,若函数用 typing 注解(如 List[str]),param.annotation 就是那个泛型类型对象,不是字符串——不能直接当 JSON 值用,也不能用 isinstance 判断。
兼容性注意点:
- Python inspect.getfullargspec(),但它不支持
**kwargs的类型注解,且返回的是命名元组而非对象 -
typing.Union、Optional等在运行时是特殊对象,需用typing.get_origin()和typing.get_args()(3.8+)解析 - 第三方库(如
pydantic)可能重写__signature__,此时inspect.signature()返回的是其定制结果,未必反映原始函数定义
真正麻烦的从来不是怎么取签名,而是拿到之后怎么统一处理各种 annotation 类型、怎么应对装饰器透传失败、以及跨版本时要不要 fallback —— 这些才是实际写工具链时卡住的地方。


















