python中的ast模块是非常强大的模块,可以用来修改python源码,或者分析python源码。为了学习ast模块,先来手动建立一个ast熟悉熟悉。
将用ast来表示下列代码:
foo = 2
if foo == 2:
print foo
else:
print "not equal"
foo=2是一个赋值语句,可以分为3个Node。foo是一个变量,=是表示赋值,2是一个常量。
foo是一个变量ast.Name
,id为变量的名字,ctx表示该变量的用途,Store即分配。
assign_foo = ast.Name(id='foo',ctx=ast.Store())
这里就得到了变量foo,用于赋值。
2是一个字面常量,可以用以下代码表示。
two = ast.Num(n=2)
赋值=
用ast.Assign
表示,targets为list表示被赋值的点,value为要给变量的值。lineno为行号,col_offset为列号。用过用函数compile()
每个Node都需要lineno,可以直接函数fix_missing_locations
,该函数会递归的添加lineno。
assign_2 = ast.Assign(targets=[copy(assign_foo)],value=copy(two))
assign_2.lineno = 1
assign_2.col_offset = 0
ast.fix_missing_locations(assign_2)
如果不用copy,完整版可以写为为:
assign_2 = ast.Assign(targets=[
ast.Name(id='foo',ctx=ast.Store())],
value=ast.Num(n=2))
assign_2.lineno = 1
assign_2.col_offset = 0
ast.fix_missing_locations(assign_2)
if语句稍微复杂些。if语句的定义为:
test为测试节点,body是一序列的节点,orelse也是一序列的节点。
class If(test, body, orelse)
use_foo = ast.Name(id='foo',ctx = ast.Load())
if_ = ast.If()
if_.test = ast.Compare()
if_.test.left = copy(use_foo)
if_.test.ops = [ast.Eq()]
if_.test.comparators = [copy(two)]
上面代码为声明了if foo==2:
,Compare的定义为
class Compare(left, ops, comparators)
其中,left为比较符号的左边,这里用foo,ops为操作符号可以为下列中的某个,这里用Eq表示==。comparators为第一个以后要比较的值(例如 1< a < 3中的1和3)。use_foo为使用变量foo,与赋值不同的是ctx为Load。
class Eq, class NotEq, class Lt, class LtE,
class Gt, class GtE, class Is, class IsNot, class In, class NotIn
if的body此处只有一句话,就是print定义为Print(dest, values, nl)
,dest为print>>dest,此处我们不需要。values要打印的值,nl为是否换行。
if_.body = [ast.Print(dest=None,values=[copy(use_foo)],nl=True)]
了解上面以后,else部分就比较简单了。其中Str为字符串常量。
if_.orelse = [ast.Print(dest=None,values=[ast.Str(s="not equal")],nl=True)]
通过调用compile函数我们就可以执行该ast了。首先建立一个Module模块,然后compile得到代码,用exec就可以执行了。
mod = ast.Module(body = [assign_2,if_])
code = compile(mod,' ','exec')
exec code
输出结果为2
如果将if判断改为 !=,NotEq,则会输出“not equal”!
ast也没有想象中那么难理解吧
可以发现,ast也只是普通的python代码,知道Node的定义以后,不难手动写出来。
完整代码https://github.com/sgoal/myBlogCodes/blob/master/ast_code/ast_foo.py