Abstract Syntax Trees即抽象语法树。Ast是python源码到字节码的一种中间产物,借助ast模块可以从语法树的角度分析源码结构。此外,我们不仅可以修改和执行语法树,还可以将Source生成的语法树unparse成python源码。因此ast给python源码检查、语法分析、修改代码以及代码调试等留下了足够的发挥空间。
1. AST简介
Python官方提供的CPython解释器对python源码的处理过程如下:
- Parse source code into a parse tree (Parser/pgen.c)
- Transform parse tree into an Abstract Syntax Tree (Python/ast.c)
- Transform AST into a Control Flow Graph (Python/compile.c)
- Emit bytecode based on the Control Flow Graph (Python/compile.c)
即实际python代码的处理过程如下:
源代码解析 --> 语法树 --> 抽象语法树(AST) --> 控制流程图 --> 字节码
上述过程在python2.5之后被应用。python源码首先被解析成语法树,随后又转换成抽象语法树。在抽象语法树中我们可以看到源码文件中的python的语法结构。
大部分时间编程可能都不需要用到抽象语法树,但是在特定的条件和需求的情况下,AST又有其特殊的方便性。
下面是一个抽象语法的简单实例。
Module(body=[ Print( dest=None, values=[BinOp( left=Num(n=1),op=Add(),right=Num(n=2))], nl=True, )])
2. 创建AST
2.1 Compile函数
先简单了解一下compile函数。
compile(source, filename, mode[, flags[, dont_inherit]])
- source -- 字符串或者AST(Abstract Syntax Trees)对象。一般可将整个py文件内容file.read()传入。
- filename -- 代码文件名称,如果不是从文件读取代码则传递一些可辨认的值。
- mode -- 指定编译代码的种类。可以指定为 exec, eval, single。
- flags -- 变量作用域,局部命名空间,如果被提供,可以是任何映射对象。
- flags和dont_inherit是用来控制编译源码时的标志。
func_def = \ """ def add(x, y): return x + y print add(3, 5) """
使用Compile编译并执行:
>>> cm = compile(func_def, '<string>', 'exec') >>> exec cm
>>> 8
上面func_def经过compile编译得到字节码,cm即code对象,True == isinstance(cm, types.CodeType)。
compile(source, filename, mode, ast.PyCF_ONLY_AST) <==> ast.
parse
(source, filename='<unknown>', mode='exec')
2.2 生成ast
使用上面的func_def生成ast.
r_node = ast.parse(func_def) print astunparse.dump(r_node) # print ast.dump(r_node)
下面是func_def对应的ast结构:
Module(body=[ FunctionDef( name='add', args=arguments( args=[Name(id='x',ctx=Param()),Name(id='y',ctx=Param())], vararg=None, kwarg=None, defaults=[]), body=[Return(value=BinOp( left=Name(id='x',ctx=Load()), op=Add(), right=Name(id='y',ctx=Load())))], decorator_list=[]), Print( dest=None, values=[Call( func=Name(id='add',ctx=Load()), args=[Num(n=3),Num(n=5)], keywords=[], starargs=None, kwargs=None)], nl=True) ])
除了ast.dump,有很多dump ast的第三方库,如astunparse, codegen, unparse等。这些第三方库不仅能够以更好的方式展示出ast结构,还能够将ast反向导出python source代码。
module Python version "$Revision$" { mod = Module(stmt* body)| Expression(expr body) stmt = FunctionDef(identifier name, arguments args, stmt* body, expr* decorator_list) | ClassDef(identifier name, expr* bases, stmt* body, expr* decorator_list) | Return(expr? value) | Print(expr? dest, expr* values, bool nl)| For(expr target, expr iter, stmt* body, stmt* orelse) expr = BoolOp(boolop op, expr* values) | BinOp(expr left, operator op, expr right)| Lambda(arguments args, expr body)| Dict(expr* keys, expr* values)| Num(object n) --