如何用Python AST提取函数调用参数并区分节点类型
问题
我正在开发一个转换函数调用的功能,需要提取函数调用及其参数。尝试用AST提取特定节点,但没法按节点类型区分它们,希望获取函数调用和参数来构建新语法。
示例代码:
import ast import argparse class Visitor(ast.NodeVisitor): def __init__(self): self.arglist = [] def visit(self, node): if isinstance(node, ast.Call): print('VVVVVVVVVVVVVV CALL ANFANG VVVVVVVVVVVVVVVV') print(f'_______ Call found ______') super().visit(node) print(f'Argumente: >>>{node.args.pop(0).__dict__}<<<<') print('______________ CALL END ________________') if isinstance(node, ast.Name): print(f'_______ Name found ______ {node.__class__}') print(f'Name Argumente: {node.id.__str__()}') return super().visit(node) elif isinstance(node, ast.For): print(f'_______ FOR found ______ ') return super().visit(node) elif isinstance(node, ast.keyword): print(f'_______ keyword found ______ {node.__class__}') return super().visit(node) elif isinstance(node, ast.Attribute): return super().visit(node) print(f'_______ Attribute fopund _____ {node.__class__}') elif isinstance(node, ast.Expr): return super().visit(node) print(f'_______ Expr found ________ {node.__class__}') elif isinstance(node, ast.Constant): print(f'_______ Konstante found!!>>>> {node.value}') return super().visit(node) return super().visit(node) tree = ast.parse(''' meat = get_mess(ONE, TWO, THREE) name2 = 'Elise' ''') print(ast.dump(tree)) vis = Visitor() vis.visit(tree)
输出结果:
Module(body=[Assign(targets=[Name(id='meat', ctx=Store())], value=Call(func=Name(id='get_mess', ctx=Load()), args=[Name(id='ONE', ctx=Load()), Name(id='TWO', ctx=Load()), Name(id='THREE', ctx=Load())], keywords=[])), Assign(targets=[Name(id='name2', ctx=Store())], value=Constant(value='Elise'))], type_ignores=[]) _______ Name found ______ <class 'ast.Name'> Name Argumente: meat VVVVVVVVVVVVVV CALL ANFANG VVVVVVVVVVVVVVVV _______ Call found ______ _______ Name found ______ <class 'ast.Name'> Name Argumente: get_mess _______ Name found ______ <class 'ast.Name'> Name Argumente: ONE _______ Name found ______ <class 'ast.Name'> Name Argumente: TWO _______ Name found ______ <class 'ast.Name'> Name Argumente: THREE Argumente: >>>{'id': 'ONE', 'ctx': <ast.Load object at 0x7f42d71fca10>, 'lineno': 2, 'col_offset': 16, 'end_lineno': 2, 'end_col_offset': 19}<<<< ______________ CALL END ________________ _______ Name found ______ <class 'ast.Name'> Name Argumente: get_mess _______ Name found ______ <class 'ast.Name'> Name Argumente: TWO _______ Name found ______ <class 'ast.Name'> Name Argumente: THREE _______ Name found ______ <class 'ast.Name'> Name Argumente: name2 _______ Konstante found!!>>>> Elise
目前代码能提取函数调用(如get_mess)和参数(如ONE、TWO、THREE),但没法按类型提取它们用于构建新代码。
解决方案
问题核心在于重写visit方法并通过isinstance判断节点类型的方式,容易导致逻辑混乱,且无法精准区分函数调用里的各个组成部分(比如函数名、位置参数、关键字参数)。
正确的做法是利用ast.NodeVisitor的特性:为特定节点类型定义visit_NodeType方法,Visitor会自动匹配对应节点,无需手动判断类型。同时,收集结构化的函数调用信息,而非仅打印,方便后续构建新语法。
修改后的代码如下:
import ast class FunctionCallVisitor(ast.NodeVisitor): def __init__(self): # 存储提取到的函数调用信息,每个元素是字典:{func_name: str, pos_args: list, kwargs: dict} self.function_calls = [] def visit_Call(self, node): # 提取函数名(这里处理简单的Name类型函数,若要支持属性调用如obj.func可扩展逻辑) func_name = node.func.id if isinstance(node.func, ast.Name) else None # 提取位置参数,支持Name和Constant类型,可扩展其他类型 pos_args = [] for arg in node.args: if isinstance(arg, ast.Name): pos_args.append(arg.id) elif isinstance(arg, ast.Constant): pos_args.append(arg.value) # 提取关键字参数 kwargs = {} for kw in node.keywords: kw_value = kw.value.id if isinstance(kw.value, ast.Name) else kw.value.value kwargs[kw.arg] = kw_value if func_name: self.function_calls.append({ 'func_name': func_name, 'pos_args': pos_args, 'kwargs': kwargs }) # 继续遍历子节点,不遗漏嵌套的函数调用 self.generic_visit(node) # 测试代码 tree = ast.parse(''' meat = get_mess(ONE, TWO, THREE) name2 = 'Elise' another_call = calculate(10, value=FOO) ''') visitor = FunctionCallVisitor() visitor.visit(tree) # 打印结构化的函数调用信息 for call in visitor.function_calls: print(f"函数名: {call['func_name']}") print(f"位置参数: {call['pos_args']}") print(f"关键字参数: {call['kwargs']}") print("-" * 30)
关键改进点:
- 使用
visit_Call方法专门处理函数调用节点,逻辑清晰,自动区分节点类型 - 结构化存储函数调用信息,方便直接用于构建新语法
- 针对不同参数类型(Name、Constant)做提取处理,可按需扩展支持更多节点类型(如Attribute、Subscript等)
- 保留
generic_visit调用,确保能遍历嵌套的函数调用(比如get_mess(foo(1), TWO)这类场景)
运行代码后会输出结构化的函数调用信息,可直接用这些数据生成新的代码语法。
内容的提问来源于stack exchange,提问作者Coliban
相关产品推荐
相关产品推荐

