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

如何用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 16:13:11