对很多人来说,AST 似乎属于编译器或工具链的领域,离日常业务开发很远。但只要你需要做静态分析、代码检查、批量改造甚至简单的代码生成,AST 就会立刻变得非常实用。
这一节我们会从 Python 标准库里的 ast 模块出发,看看源码为什么可以被解析成结构化数据,又如何在这个基础上实现分析、变换与生成。理解这一层,也会让你对语言本身有一种新的视角。
前置知识:代码为什么可以被“当作数据”处理?
我们平时把代码当作“让程序运行的文本”,但从工具链视角看,它首先是一种有语法规则的结构化语言。
只要能把源代码解析成语法树,程序就可以不再只是执行代码,还能:
- 分析代码;
- 统计代码;
- 修改代码;
- 重新生成代码。
所以 AST 的意义,不在于炫技,而在于让代码第一次以“数据结构”的形式出现在你面前。
Python ast 模块的基本使用
Python 标准库已经提供了 ast 模块。它可以把一段源码解析成语法树节点。
你不一定要一开始就记住所有节点类型,更重要的是先接受一个新心智:一段 def、一个 if、一个函数调用,在 AST 里都有明确结构,而不是只是一串文本。
这意味着你做代码分析时,不需要靠字符串查找硬猜逻辑,而可以基于真实语法结构来判断。
例如:
import ast
source = """
def hello(name):
print(name)
"""
tree = ast.parse(source)
print(ast.dump(tree, indent=2))第一次看到树状结果时,你会明显感受到:代码已经不再只是文本,而是被拆成了可遍历、可分析的节点结构。
读取、遍历与分析语法树
拿到语法树之后,最常见的动作通常是遍历。
遍历的意义在于,你可以系统地找到:
- 所有函数定义;
- 所有导入语句;
- 某类调用表达式;
- 某类写法是否出现。
所以 AST 在静态检查里的价值非常直接。你不再只能问“某个文本有没有出现”,而是可以问“某种语法结构有没有出现”。
下面这个例子,就是最小的函数收集器:
import ast
from pathlib import Path
class FunctionCollector(ast.NodeVisitor):
def __init__(self):
self.names = []
def visit_FunctionDef(self, node):
self.names.append(node.name)
self.generic_visit(node)
tree = ast.parse(Path("demo.py").read_text(encoding="utf-8"))
collector = FunctionCollector()
collector.visit(tree)
print(collector.names)这时你统计的已经不是“文本里出现了 def”,而是真正的函数定义节点。
修改 AST 并生成新代码
AST 更进一步的能力,是变换。
也就是说,你不仅能看懂代码结构,还可以:
- 定位旧写法;
- 替换成新写法;
- 再输出成新代码。
这使得批量改造成为可能。很多过去靠手工搜改、风险极高的工作,只要规则足够明确,就能被工具化。
当然也要注意,代码生成和改写的难点从来不只是“改出来”,而是改完之后是否还保持语义正确、可读性稳定。
如果要做最小改写,可以先从 NodeTransformer 开始:
import ast
class ReplaceFoo(ast.NodeTransformer):
def visit_Name(self, node):
if node.id == "foo":
return ast.copy_location(ast.Name(id="bar", ctx=node.ctx), node)
return node
tree = ast.parse("print(foo)")
new_tree = ReplaceFoo().visit(tree)
ast.fix_missing_locations(new_tree)
print(ast.unparse(new_tree))输出会变成:
print(bar)这就是批量改造工具最小可行原型的雏形。
实战场景:静态检查、批量改造与代码生成
示例:函数收集
一个非常典型的小工具,就是统计某个项目里定义了哪些函数、分布在哪些文件。这类需求如果靠字符串查找容易误判,但 AST 可以更准确识别真正的函数定义节点。
示例:自动替换旧写法
另一个很实用的场景,是批量迁移旧 API 调用。只要你能明确识别旧调用模式,并知道新目标结构,就有机会通过 AST 做更安全的自动替换。
所以 AST 的价值并不只在“编译器相关”,它其实非常适合进入工程工具链、代码治理和批量重构场景。
真正开始动手之后,你会发现 AST 最适合做的是“规则明确、结构稳定、可批量执行”的修改,而不是替代你做所有复杂重构决策。
总结
这一节我们用更底层的视角重新看待了 Python 代码本身,理解了 AST 不只是编译器世界里的概念,也可以成为静态检查、代码迁移和工具开发的基础。换一个层次理解语言,往往会带来完全不同的能力边界。
下一节我们会把整套专题中的高频知识点重新收束回面试场景,看看哪些问题最值得重点准备,又该如何回答得更有层次。
扩展阅读
如果你对这部分内容产生了兴趣,后续很适合继续沿着这些方向深入:
- 看看 Ruff、Black、mypy 这类工具背后分别在哪一层做分析;
- 进一步了解 CST、格式保留改写与 CodeMod 工具链;
- 把一个小型批量改造脚本真正落地到自己的项目里练一次。