Python Ast介绍及应用 - 博客园
文章推薦指數: 80 %
Ast是python源码到字节码的一种中间产物,借助ast模块可以从语法树的角度分析源码结构。
此外,我们不仅可以修改和执行语法树,还可以将Source生成的语法树 ...
首页
新闻
博问
专区
闪存
班级
我的博客
我的园子
账号设置
简洁模式...
退出登录
注册
登录
alpha_panda
简单精致,吾之所向!深入专注,吾之所行!
PythonAst介绍及应用
AbstractSyntaxTrees即抽象语法树。
Ast是python源码到字节码的一种中间产物,借助ast模块可以从语法树的角度分析源码结构。
此外,我们不仅可以修改和执行语法树,还可以将Source生成的语法树unparse成python源码。
因此ast给python源码检查、语法分析、修改代码以及代码调试等留下了足够的发挥空间。
1.AST简介
Python官方提供的CPython解释器对python源码的处理过程如下:
Parsesourcecodeintoaparsetree(Parser/pgen.c)
TransformparsetreeintoanAbstractSyntaxTree(Python/ast.c)
TransformASTintoaControlFlowGraph(Python/compile.c)
EmitbytecodebasedontheControlFlowGraph(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.1Compile函数
先简单了解一下compile函数。
compile(source,filename,mode[,flags[,dont_inherit]])
source--字符串或者AST(AbstractSyntaxTrees)对象。
一般可将整个py文件内容file.read()传入。
filename--代码文件名称,如果不是从文件读取代码则传递一些可辨认的值。
mode--指定编译代码的种类。
可以指定为exec,eval,single。
flags--变量作用域,局部命名空间,如果被提供,可以是任何映射对象。
flags和dont_inherit是用来控制编译源码时的标志。
func_def=\
"""
defadd(x,y):
returnx+y
printadd(3,5)
"""
使用Compile编译并执行:
>>>cm=compile(func_def,'
compile(source,filename,mode,ast.PyCF_ONLY_AST) <==> ast.parse(source,filename='
这些第三方库不仅能够以更好的方式展示出ast结构,还能够将ast反向导出pythonsource代码。
modulePythonversion"$Revision$"
{
mod=Module(stmt*body)|Expression(exprbody)
stmt=FunctionDef(identifiername,argumentsargs,stmt*body,expr*decorator_list)
|ClassDef(identifiername,expr*bases,stmt*body,expr*decorator_list)
|Return(expr?value)
|Print(expr?dest,expr*values,boolnl)|For(exprtarget,expriter,stmt*body,stmt*orelse)
expr=BoolOp(boolopop,expr*values)
|BinOp(exprleft,operatorop,exprright)|Lambda(argumentsargs,exprbody)|Dict(expr*keys,expr*values)|Num(objectn)--anumberasaPyObject.
|Str(strings)--needtospecifyraw,unicode,etc?|Name(identifierid,expr_contextctx)
|List(expr*elts,expr_contextctx)
--col_offsetisthebyteoffsetintheutf8stringtheparseruses
attributes(intlineno,intcol_offset)
expr_context=Load|Store|Del|AugLoad|AugStore|Param
boolop=And|Or
operator=Add|Sub|Mult|Div|Mod|Pow|LShift|RShift|BitOr|BitXor|BitAnd|FloorDiv
arguments=(expr*args,identifier?vararg,identifier?kwarg,expr*defaults)
}
ViewCode
上面是部分摘自官网的 AbstractGrammar,实际遍历astNode过程中根据Node的类型访问其属性。
3.遍历AST
python提供了两种方式来遍历整个抽象语法树。
3.1ast.NodeTransfer
将func_def中的add函数中的加法运算改为减法,同时为函数实现添加调用日志。
1classCodeVisitor(ast.NodeVisitor):
2defvisit_BinOp(self,node):
3ifisinstance(node.op,ast.Add):
4node.op=ast.Sub()
5self.generic_visit(node)
6
7defvisit_FunctionDef(self,node):
8print'FunctionName:%s'%node.name
9self.generic_visit(node)
10func_log_stmt=ast.Print(
11dest=None,
12values=[ast.Str(s='callingfunc:%s'%node.name,lineno=0,col_offset=0)],
13nl=True,
14lineno=0,
15col_offset=0,
16)
17node.body.insert(0,func_log_stmt)
18
19r_node=ast.parse(func_def)
20visitor=CodeVisitor()
21visitor.visit(r_node)
22#printastunparse.dump(r_node)
23printastunparse.unparse(r_node)
24execcompile(r_node,'
既然func_def中定义的add已经被改成一个减函数了,那么我们就彻底一点,把函数名和参数以及被调用的函数都在ast中改掉,并且将添加的函数调用log写的更加复杂一些,争取改的面目全非:-)
1classCodeTransformer(ast.NodeTransformer):
2defvisit_BinOp(self,node):
3ifisinstance(node.op,ast.Add):
4node.op=ast.Sub()
5self.generic_visit(node)
6returnnode
7
8defvisit_FunctionDef(self,node):
9self.generic_visit(node)
10ifnode.name=='add':
11node.name='sub'
12args_num=len(node.args.args)
13args=tuple([arg.idforarginnode.args.args])
14func_log_stmt=''.join(["print'callingfunc:%s',"%node.name,"'args:'",",%s"*args_num%args])
15node.body.insert(0,ast.parse(func_log_stmt))
16returnnode
17
18defvisit_Name(self,node):
19replace={'add':'sub','x':'a','y':'b'}
20re_id=replace.get(node.id,None)
21node.id=re_idornode.id22self.generic_visit(node)
23returnnode
24
25r_node=ast.parse(func_def)
26transformer=CodeTransformer()
27r_node=transformer.visit(r_node)
28#printastunparse.dump(r_node)
29source=astunparse.unparse(r_node)
30printsource
31#execcompile(r_node,'
这里不再赘述。
4.AST应用
AST模块实际编程中很少用到,但是作为一种源代码辅助检查手段是非常有意义的;语法检查,调试错误,特殊字段检测等。
上面通过为函数添加调用日志的信息是一种调试python源代码的一种方式,不过实际中我们是通过parse整个python文件的方式遍历修改源码。
4.1汉字检测
下面是中日韩字符的unicode编码范围
CJKUnifiedIdeographs
Range: 4E00— 9FFF
Numberofcharacters: 20992
Languages: chinese,japanese,korean,vietnamese
使用unicode范围 \u4e00-\u9fff 来判别汉字,注意这个范围并不包含中文字符(e.g. u';'== u'\uff1b') .
下面是一个判断字符串中是否包含中文字符的一个类CNCheckHelper:
1classCNCheckHelper(object):
2#待检测文本可能的编码方式列表
3VALID_ENCODING=('utf-8','gbk')
4
5def_get_unicode_imp(self,value,idx=0):
6ifidx
延伸文章資訊
- 1Python ast 模块使用
- 2Python AST 抽象语法树 - 简书
因此ast给python源码检查、语法分析、修改代码以及代码调试等留下了足够的发挥空间。 1. AST简介. Python官方提供的CPython解释器对python源码的处理过程如下:. Pa...
- 3Python ast.NodeTransformer用法及代碼示例- 純淨天空
用法: class ast.NodeTransformer. NodeVisitor 子類遍曆抽象語法樹並允許修改節點。 NodeTransformer 將遍曆AST 並使用訪問者方法的返回值來...
- 4AST 模块:用Python 修改Python 代码 - PyCoder's Weekly ...
在这篇文章中,我们将看到如何使用 ast 模块对Python 代码进行修改,同时还将看到一些使用了这个技术的工具。 CPython 的编译过程¶. http://pyimg.fanhe.org/...
- 5ast --- 抽象语法树— Python 3.8.13 說明文件
ast 模块帮助Python 程序处理Python 语法的抽象语法树。抽象语法或许会随着Python 的更新发布而改变;该模块能够帮助理解当前语法在编程层面的样貌。