如何在不使用inspect的情况下静态构造Python函数Signature对象?
静态生成Python函数Signature对象的方案
不需要导入目标代码的话,用Python标准库的ast模块做静态语法分析是最简便可行的方案,以下是具体实现思路和代码:
核心思路
- 读取目标Python文件的文本内容,用
ast.parse()解析成抽象语法树(AST) - 遍历AST找到所有函数/方法定义节点(
FunctionDef或AsyncFunctionDef) - 从节点中提取参数名、类型注解、默认值、返回值注解等信息
- 用
inspect.Signature和inspect.Parameter构造对应的Signature对象
代码实现
import ast import inspect from typing import Optional, List def build_signature(func_node: ast.FunctionDef) -> inspect.Signature: params = [] # 处理普通参数(含self/cls) for i, arg in enumerate(func_node.args.args): # 提取参数名 param_name = arg.arg # 提取类型注解 annotation = inspect.Parameter.empty if arg.annotation is not None: try: annotation = ast.unparse(arg.annotation) except AttributeError: # Python 3.8及以下可替换为astor.to_source(arg.annotation) annotation = ast.dump(arg.annotation, compact=True) # 提取默认值 default = inspect.Parameter.empty if i >= len(func_node.args.args) - len(func_node.args.defaults): default_node = func_node.args.defaults[i - (len(func_node.args.args) - len(func_node.args.defaults))] try: default = ast.literal_eval(default_node) except (ValueError, SyntaxError): # 无法静态求值的默认值,用字符串形式保留 default = ast.unparse(default_node) # 构造参数对象 params.append(inspect.Parameter( name=param_name, kind=inspect.Parameter.POSITIONAL_OR_KEYWORD, default=default, annotation=annotation )) # 处理*args可变参数 if func_node.args.vararg: vararg_name = func_node.args.vararg.arg vararg_annotation = inspect.Parameter.empty if func_node.args.vararg.annotation is not None: vararg_annotation = ast.unparse(func_node.args.vararg.annotation) params.append(inspect.Parameter( name=vararg_name, kind=inspect.Parameter.VAR_POSITIONAL, annotation=vararg_annotation )) # 处理**kwargs可变参数 if func_node.args.kwarg: kwarg_name = func_node.args.kwarg.arg kwarg_annotation = inspect.Parameter.empty if func_node.args.kwarg.annotation is not None: kwarg_annotation = ast.unparse(func_node.args.kwarg.annotation) params.append(inspect.Parameter( name=kwarg_name, kind=inspect.Parameter.VAR_KEYWORD, annotation=kwarg_annotation )) # 处理返回值注解 return_annotation = inspect.Parameter.empty if func_node.returns is not None: try: return_annotation = ast.unparse(func_node.returns) except AttributeError: return_annotation = ast.dump(func_node.returns, compact=True) return inspect.Signature(parameters=params, return_annotation=return_annotation) # 示例用法 if __name__ == "__main__": # 用示例代码字符串代替读取文件内容 code = """ def method_with_args(self, param_a: str, param_b: int = 123) -> str: \"\"\" Method with arguments and a return value. Args: param_a (str): A string parameter. param_b (int): An integer parameter. Returns: str: Concatenation of param1 and string representation of param2. \"\"\" return param_a + str(param_b) """ tree = ast.parse(code) for node in ast.walk(tree): if isinstance(node, ast.FunctionDef): sig = build_signature(node) print(sig)
注意事项
- 对于无法静态求值的默认值(比如
param = some_function()),ast.literal_eval会失败,此时代码会用ast.unparse将默认值的AST节点转换成字符串表示,保证Signature结构完整 - Python 3.9及以上支持
ast.unparse(),3.8及以下版本可安装astor库,用astor.to_source()替代 - 如果需要处理复杂语法(比如泛型注解、类型变量),可以考虑第三方静态分析库
libcst,它对Python语法的处理更鲁棒
第三方库可选方案(libcst)
import libcst import inspect def build_signature_with_libcst(code: str) -> inspect.Signature: module = libcst.parse_module(code) params = [] return_annotation = inspect.Parameter.empty for node in module.body: if isinstance(node, libcst.FunctionDef): # 处理普通参数 for param in node.params.params: param_name = param.name.value # 提取类型注解 annotation = inspect.Parameter.empty if param.annotation: annotation = libcst.Module([param.annotation.code]).code # 提取默认值 default = inspect.Parameter.empty if param.default: default = libcst.Module([param.default.code]).code params.append(inspect.Parameter( name=param_name, kind=inspect.Parameter.POSITIONAL_OR_KEYWORD, default=default, annotation=annotation )) # 处理返回值注解 if node.returns: return_annotation = libcst.Module([node.returns.code]).code return inspect.Signature(parameters=params, return_annotation=return_annotation)
内容的提问来源于stack exchange,提问作者Kiarahmani
相关产品推荐
相关产品推荐

