python ast
### 1. 它是什么Python AST全称是Abstract Syntax Tree抽象语法树。简单说就是Python解释器在读懂你的代码之后、真正开始执行之前生成的一种中间表示形式。它像一张地图把代码里的每一个元素——变量、函数、循环、条件——都变成了树上的节点。这棵树不是最终的执行结果却包含了代码全部的结构信息。举个例子你写了一句代码a 1 2。在解释器眼里这可不是简单的赋值和加法而是一个Assign节点里面包着一个Add节点里面再有Num节点。就像医生看一张医学影像能看到皮下的骨骼和血管。AST就是那个影像帮你看到代码底层的骨架。2. 它能做什么有了这棵树你会发现原来代码是可以被“操作”的。只要拿到这棵树的节点你就可以做静态分析检查代码里有没有潜在的问题比如未使用的变量、有风险的函数调用甚至找出那些不该出现的敏感信息。修改代码结构在树里增减节点相当于在不碰源代码的情况下改变代码的逻辑。比如你想给所有函数调用自动加上日志AST可以直接把函数调用节点替换成带日志的包装版本。生成代码你写一个模板AST帮你在树上自动生成节点就像搭积木一样造出新的代码。编译器的代码生成就是这么干的。实现自定义语法比如你想支持某种宏或者语法糖先把它变成AST节点再走一遍常规流程即可。现实中我最常遇到的场景是写爬虫或者数据处理时需要动态生成很多相似的函数。手动写几百行重复代码既不优雅也不环保用AST能省下不少功夫而且修改起来也方便。3. 怎么使用Python的ast模块提供了很直接的接口。大致分三步走第一步把源代码变成树importast coderesult [x**2 for x in range(10) if x % 2 0]treeast.parse(code)ast.parse接受代码字符串返回AST的根节点。这棵树的根是Module下面可能跟着Assign、ListComp、IfExp等等。第二步遍历或修改树你用的是ast.NodeVisitor只读或ast.NodeTransformer可修改。前者适合做分析后者适合改写。比如我想找出代码里所有的变量名classFindNames(ast.NodeVisitor):defvisit_Name(self,node):print(fFound variable:{node.id})treeast.parse(x 42\nprint(y))FindNames().visit(tree)想把所有变量名改成“secret”classRename(ast.NodeTransformer):defvisit_Name(self,node):node.idsecretreturnnode treeRename().visit(tree)注意修改树后必须显式地返回新的节点否则修改不生效。这是很多人容易忽略的细节。第三步把树变回代码调用ast.unparsenew_codeast.unparse(tree)会生成格式有点粗糙的代码但逻辑完全保留。如果想控制格式可以用ast.dump先看看树的结构。4. 最佳实践AST最需要留意的是它是一把双刃剑。用得好能让代码少写一半用不好会把项目变成一堆难以调试的魔法。第一不要滥用。如果你的需求只是做简单的统计或检查ast的解析效率远比正则高但如果你只需要提取类名或函数名可能inspect模块就够了。AST的开销是值得的前提是你的任务确实需要“理解结构”而不是“匹配字符串”。第二把AST操作封装成独立的工具函数。不要把AST逻辑散落在业务代码里。写一个专门的模块负责把源文件读进来、解析、修改、写回去。这样别人包括未来的自己想改时不会在业务逻辑的缝隙里找AST代码。第三小心节点类型变更。Python的AST结构并不稳定不同版本的Python之间节点的属性名、字段名都可能换。如果写一个生产级别的AST工具最好在代码里加个版本检测或者用sys.version_info判断。第四测试要覆盖特殊情况。比如遇到语法错误时ast.parse会抛出SyntaxError。养成习惯把解析过程包在try-except里并且不要吞掉错误——至少打日志否则出问题时无处可查。第五处理代码源信息。AST的节点自带lineno和col_offset属性可以用来定位修改前后的代码位置。这在生成错误消息、调试工具里特别有用。你把代码改了后最好更新一下这些偏移量不然出错时指向的行号不准确很难排查。5. 和同类技术对比说到同类技术最直接的是正则表达式。很多人图省事用正则找函数定义或变量赋值。但正则只能做浅层匹配你没法确认它是不是真的函数定义万一代码里有注释或字符串呢AST是深度理解的它能区分出注释和代码也不会被缩进、换行打乱。正则适合抓取并快速替换AST适合做有逻辑的修改。另一个是**lib2to3或redbaron**这些库提供了更高级的API来操作目标代码。lib2to3一度是官方用来做Python 2到3转换的工具但它强在“不懂代码也能操作”如果你需要保留原始格式和注释lib2to3是首选。相比之下AST丢弃了注释、缩进、换行等格式信息单纯的ast.unparse可能不会保留它们。所以如果你的任务是重构工具或者想在不改变代码样式的前提下修改lib2to3可能更合适如果你只想做逻辑分析或生成新的代码AST更简洁稳定。还有个是**astroid**这是pylint背后的解析器。它在Python的ast基础上做了扩展支持了类型推断、引用解析等。比如你想找出一个函数里到底用了哪些全局变量用原生AST只能通过名字来猜astroid能帮你追溯到变量的来源。这是静态分析工具的进阶之选。最后提一下Jedi或Pyright这类支持IDE智能感知的引擎它们内部也大量使用了AST但功能更全面包含符号表、类型检查等。如果你只是想做代码的简单静态修改没必要用这么重的依赖。简单说如果只是日常的代码分析、自定义转换Python自带的ast是首选如果需要保留格式和注释或做更复杂、规模更大的静态分析才考虑其他库。核心是衡量清楚“我要啃骨头的深度”别用原子弹炸蚊子。