python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > python ast 模块

Python AST 模块实战演示

作者:烟雨AC

Python的ast模块提供了一种处理Python代码的强大工具,通过解析代码生成抽象语法树(AST),可以进行代码分析、修改和生成,接下来通过本文给大家介绍Python AST 模块实战教程,感兴趣的朋友跟随小编一起看看吧

Python 的 ast(Abstract Syntax Tree,抽象语法树)模块是一个用于处理 Python 代码的强大工具。它允许你将代码转换为一种结构化的树形表示,从而可以进行分析、修改甚至生成新的代码。这对于理解代码的内部结构、构建开发工具或进行代码自动化处理非常有帮助。

🔍 什么是抽象语法树(AST)

在深入了解 ast 模块之前,我们先简单理解一下抽象语法树(AST)的概念。当 Python 解释器执行代码时,它首先需要理解代码的结构。这个过程大致是:源代码 -> 词法分析(生成令牌流) -> 语法分析(生成 AST) -> 字节码 -> 执行。AST 就是源代码抽象语法结构的树状表示,它过滤掉了像空格、注释这类非本质的细节,专注于代码的逻辑结构。每个节点代表代码中的一个结构(例如,一个表达式、一个语句、一个函数定义等)。

ast 模块的核心作用就是在这棵“语法树”上工作,让你能在代码被编译成字节码之前,洞察和操作其本质。

🛠️ ast 模块的核心用法

ast 模块提供了一系列函数和类来创建、遍历和修改 AST。

1. 解析代码生成 AST

使用 ast.parse() 函数可以将字符串形式的 Python 代码解析成一棵 AST 的根节点(通常是 ast.Module 节点)。

import ast
code = """
def greet(name):
    print(f"Hello, {name}!")
"""
tree = ast.parse(code)  # 得到 AST

2. 查看 AST 结构

生成 AST 后,可以使用 ast.dump() 函数将其以文本形式打印出来,以便查看整个树的结构。

print(ast.dump(tree, indent=4))

这会输出一个结构化的文本,展示所有的节点、它们的属性以及嵌套关系。

3. 遍历 AST

要分析 AST,你需要遍历它的节点。ast 模块提供了两种主要方式:

class MyVisitor(ast.NodeVisitor):
    def visit_FunctionDef(self, node):
        print(f"Found function: {node.name}")
        self.generic_visit(node)  # 继续遍历子节点
visitor = MyVisitor()
visitor.visit(tree)
for node in ast.walk(tree):
    if isinstance(node, ast.FunctionDef):
        print(node.name)

4. 修改 AST

除了分析,你还可以修改 AST。这需要通过继承 ast.NodeTransformer 类来实现。它的使用方式和 NodeVisitor 类似,但关键区别在于,visit_ 方法需要返回一个节点。你可以返回:

一个经典的例子是将代码中的所有加法操作 + 替换为减法操作 -

class AddToSubTransformer(ast.NodeTransformer):
    def visit_BinOp(self, node):
        if isinstance(node.op, ast.Add):
            # 创建一个新的操作符节点,将加法改为减法
            node.op = ast.Sub()
        return self.generic_visit(node)  # 返回修改后的节点,并继续遍历其子节点
transformer = AddToSubTransformer()
new_tree = transformer.visit(tree)

重要提示:在创建新节点替换旧节点后,如果新节点缺少源代码位置信息(如行号、列偏移量),需要使用 ast.fix_missing_locations() 函数来修复,否则在编译时可能会出错。

5. 将 AST 编译回代码

修改完 AST 后,你可以通过内置的 compile() 函数将其编译成可执行的 Python 字节码,然后使用 exec()eval() 来运行它。

# 将修改后的 AST 编译成代码对象
code_obj = compile(new_tree, filename='<string>', mode='exec')
# 执行代码对象
exec(code_obj)

此外,从 Python 3.9 开始,标准库提供了 ast.unparse() 函数,可以直接将 AST 节点转换回可读的 Python 代码字符串。对于更早的版本,可以使用第三方库如 astor

6. 安全地求值表达式

ast 模块还提供了一个非常实用的函数 ast.literal_eval()。它可以安全地计算一个包含 Python 字面量(如字符串、数字、元组、列表、字典、布尔值等)的表达式字符串,并返回结果。与内置的 eval() 不同,它不会执行任意代码,因此安全得多,非常适合处理来自不可信来源的数据。

safe_result = ast.literal_eval("[1, 2, 3]")  # 结果是列表 [1, 2, 3]

💻 大厂笔试面试如何考察 ast

在大厂的笔试或面试中,对 ast 模块的考察通常不会要求你死记硬背所有的节点类型,而是更注重实践应用能力对 Python 机制的理解深度以及解决实际问题的思路

常见的考察方向

实例分析:面试题模拟

题目:请你使用 ast 模块,编写一个简单的静态分析工具,用于检测一段 Python 代码中是否使用了 eval() 函数。如果使用了,则输出警告信息。

考察点

参考实现

import ast
code = """
x = 1
result = eval('1 + 1')
print(eval('2+2'))
"""
class EvalDetector(ast.NodeVisitor):
    def visit_Call(self, node):
        # 检查节点是否是一个函数调用,并且函数名是一个标识符(Name)且id为'eval'
        if isinstance(node.func, ast.Name) and node.func.id == 'eval':
            print(f"Warning: Potential use of eval() found at line {node.lineno}")
        # 继续遍历子节点,以查找嵌套调用等情况
        self.generic_visit(node)
tree = ast.parse(code)
detector = EvalDetector()
detector.visit(tree)

输出

Warning: Potential use of eval() found at line 4
Warning: Potential use of eval() found at line 5

这个例子演示了如何使用 ast.NodeVisitor 来访问代码中的函数调用节点(ast.Call),并根据条件(函数名为 eval)进行判断和输出。

💎 总结与核心知识点

为了帮助你更好地记忆,我将 ast 模块的核心知识点整理成了下面的表格:

核心概念/操作关键函数/类说明与用途
解析代码ast.parse(source)将源代码字符串解析为 AST 根节点(ast.Module)。
查看结构ast.dump(node)将 AST 节点以字符串形式输出,用于调试。
遍历 ASTast.NodeVisitor通过继承此类并定义 visit_XXX 方法来有选择地访问节点。
遍历 ASTast.walk(node)递归遍历 AST 中的所有节点,不保留层级信息。
修改 ASTast.NodeTransformer通过继承此类,在 visit_XXX 方法中返回新节点来修改 AST。
编译执行compile(tree, ...)将 AST 编译为可执行的代码对象。
反解析ast.unparse(node) (Python 3.9+)将 AST 节点转换回等价的 Python 代码字符串。
安全求值ast.literal_eval()安全地求值字面量表达式(字符串、列表、数字等),避免任意代码执行风险。
修复位置ast.fix_missing_locations(node)为新建或修改的节点补充行号等位置信息,确保能正确编译。

到此这篇关于Python AST 模块实战解析的文章就介绍到这了,更多相关python ast 模块内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文