You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获取Python函数的真实签名?规避inspect.signature的误导

摘要

inspect.signature可获取函数签名,但该签名可能存在误导(通过为函数的__signature__属性赋值即可发现)。

是否存在Python内置函数(优先内置)可获取函数的真实签名?

背景补充

inspect.Parameter.empty被用作参数可选属性kind和default的取值。若在函数定义中使用它,函数的行为有时会与省略该参数时一致,有时则不同。

请参考以下两个函数:

from inspect import Parameter, signature

empty = Parameter.empty

def chalk(x=empty, y=2):
    return True

def cheese(x, y=2):
    return True

它们的签名(包括help中显示的签名)完全相同:

assert signature(chalk) == signature(cheese)
assert str(signature(chalk)) == str(signature(cheese)) =='(x, y=2)'

但从参数提示的角度(在功能丰富的IDE中输入函数参数时的提示)来看,二者并不相同。在VsCode中:

  • chalk的提示为(x: empty = empty, y: int = 2) -> Literal[True],显示x带有默认值。
  • cheese的提示为(x: Any, y: int = 2) -> Literal[True],显示x无默认值。

inspect.signature本质上返回的是函数__signature__属性的值,该值与函数的实际行为完全无关。

VsCode的签名提示信息源自何处?是否存在Python内置函数(优先内置)可获取这种真实签名?


问题解答

不存在Python内置函数直接获取这种所谓的"真实"签名,但可以通过解析函数的底层元数据或AST(抽象语法树)来还原定义层面的参数信息。

VSCode提示的来源

VSCode的Python插件(基于Pylance)并不依赖inspect.signature,而是直接读取函数的__code__属性或解析AST:

  • 函数的__code__属性包含字节码、参数数量、默认值集合等底层元数据。比如chalk.__code__.co_defaults是(empty, 2),而cheese.__code__.co_defaults是(2,),这就是两者定义层面的核心差异。
  • Pylance结合这些元数据和类型推断,生成更贴合原始定义的参数提示。

获取"真实"签名的方法

  1. 解析函数AST
import ast
import inspect

def get_real_param_info(func):
    source = inspect.getsource(func)
    tree = ast.parse(source)
    func_def = tree.body[0]
    param_details = []
    for param in func_def.args.args:
        info = {"name": param.arg}
        if param.default is not None:
            info["has_default"] = True
            info["default_expr"] = ast.dump(param.default)
        else:
            info["has_default"] = False
        param_details.append(info)
    return param_details

# 测试
print(get_real_param_info(chalk))
print(get_real_param_info(cheese))
  1. 读取__code__属性
def get_raw_signature(func):
    arg_count = func.__code__.co_argcount
    default_count = len(func.__code__.co_defaults) if func.__code__.co_defaults else 0
    param_names = func.__code__.co_varnames[:arg_count]
    
    params = []
    for idx, name in enumerate(param_names):
        if idx >= arg_count - default_count:
            default_val = func.__code__.co_defaults[idx - (arg_count - default_count)]
            params.append(f"{name}={default_val}")
        else:
            params.append(name)
    return f"({', '.join(params)})"

# 测试
print(get_raw_signature(chalk))
print(get_raw_signature(cheese))

需要注意的是,inspect.signature将Parameter.empty视为"无默认值",是因为它模拟的是Python的参数解析行为——调用chalk()和cheese()都会抛出TypeError,所以签名被判定为一致。但从函数定义的原始信息来看,两者确实存在差异,这就是IDE提示不同的原因。

内容的提问来源于stack exchange,提问作者thorwhalen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 08:11:19