Python AST 抽象语法树 - 简书

文章推薦指數: 80 %
投票人數:10人

因此ast给python源码检查、语法分析、修改代码以及代码调试等留下了足够的发挥空间。

1. AST简介. Python官方提供的CPython解释器对python源码的处理过程如下:. Parse ... PythonAST抽象语法树AbstractSytaxTree 暂时用到的原因:在模型量化中,需要量化某些操作符带来的运算效果,比如'+','-','*','/'等等,这些就需要对源代码进行查询,因此就要需要将python解释器已经将源代码转化为运行的类后,再翻转回源代码 参考: https://docs.python.org/3/library/ast.html#ast.NodeTransformer https://www.cnblogs.com/yssjun/p/10069199.html 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又有其特殊的方便性。

下面是一个抽象语法的简单实例。

func_def=\ """ defadd(x,y): returnx+y print(add(3,5)) """ print(func_def) 其中三引号可以根据书写的方式智能换行,输出如下: defadd(x,y): returnx+y print(add(3,5)) image.png 2.创建AST 2.1compile(source,filename,mode[,flags[,dont_inherit]]) 这是python自带的函数 source--字符串或者AST(AbstractSyntaxTrees)对象。

一般可将整个py文件内容file.read()传入。

filename--代码文件名称,如果不是从文件读取代码则传递一些可辨认的值。

mode--指定编译代码的种类。

可以指定为exec,eval,single。

flags--变量作用域,局部命名空间,如果被提供,可以是任何映射对象。

flags和dont_inherit是用来控制编译源码时的标志。

>>>cm=compile(func_def,filename='',mode='exec') >>>exec(cm) 8 >>>type(cm) code 上面func_def经过compile编译得到字节码,cm即code对象,True==isinstance(cm,types.CodeType)。

2.2生成AST >>>cm1=ast.parse(func_def,filename='',mode='exec') >>>type(cm1) _ast.Module >>>ast.dump(cm1) ( body=[ FunctionDef(name='add', args=arguments( args=[arg(arg='x',annotation=None),arg(arg='y',annotation=None)], vararg=None,kwonlyargs=[],kw_defaults=[],kwarg=None,defaults=[] ), body=[Return( value=BinOp(left=Name(id='x',ctx=Load()),op=Add(),right=Name(id='y',ctx=Load())) ) ], decorator_list=[], returns=None), Expr(value=Call( func=Name(id='print',ctx=Load()), args=[Call(func=Name(id='add',ctx=Load()),args=[Num(n=3),Num(n=5)],keywords=[])], keywords=[]) ) ] ) 可以看到,这里对源代码进行了解析 首先是源代码字符串的主体body,可以看到,一个是FunctionDef,也就是我们定义的add函数,另外一个是下面使用的print函数 对于第一个主体FunctionDef,可以看到里面的name是‘add’,也就是函数的名字是add,再一个就是args,参数,可以看到一个是'x',annotation=None,另外一个参数是y,annntation=None;然后里面又有一个body,里面可以看到是return返回值,其中BinOp表示双目操作符,操作符的左值为x,操作符op为Add(),也就是将我们源代码中的+转换成了Add()函数,最后就是右值y 最后就是print函数,可以看到,values是Call调用了一个函数,其中函数名func为add,参数有两个,一个是3,一个是5 3.遍历语法树 python提供了两种方式来遍历整个语法树 节点的访问就只需要重写visit_nodename函数,在里面定义参数即可 这里节点的visit会默认根据ast中的nodename去访问visit_nodename函数,同时如果当前节点存-在children,比如FunctionDef中存在BinOp节点,若想visitBinOp这个节点,就需要在FunctionDef中增加一句self.generic_visit()来达到递归访问;如果不加,就只能访问当前节点 generic_visit(node) Thisvisitorcallsvisit()onallchildrenofthenode.Notethatchildnodesofnodesthathaveacustomvisitormethodwon’tbevisitedunlessthevisitorcallsgeneric_visit()orvisitsthemitself. 3.1ast.NodeVisitor 比如我们将func_def的add函数中的加法运算改为减法 classCodeVisitor(ast.NodeVisitor): defvisit_BinOp(self,node):#这个函数的访问是由于Visit_FunctionDef的先访问再generic_visit才访问的 print('Bin')#如果Visit_FunctionDef中没有generic_visit的话,则这个函数是不会访问的 ifisinstance(node.op,ast.Add): node.op=ast.Sub() self.generic_visit(node) defvisit_FunctionDef(self,node): print('FunctionName:%s'%node.name) self.generic_visit(node)#FunctionDef中还包含有BinOp,因此会进去visitBinOP defvisit_Call(self,node): print("call") self.generic_visit(node)#因为AST的Call中还包含有一个Call,因此会重复再访问一次 r_node=ast.parse(func_def) visitor=CodeVisitor() visitor.visit(r_node)#这里的visit函数会根据node的语法树去遍历里面的函数, 输出: FunctionName:add Bin call call 3.2ast.NodeTransformer ANodeVisitorsubclassthatwalkstheabstractsyntaxtreeandallowsmodificationofnodes 使用NodeVisitor主要是通过修改语法树上节点的方式改变AST结构,NodeTransformer主要是替换ast中的节点。

classCodeTransformer(ast.NodeTransformer): defvisit_BinOp(self,node): ifisinstance(node.op,ast.Add): node.op=ast.Sub() self.generic_visit(node) returnnode defvisit_FunctionDef(self,node): self.generic_visit(node)#这里表示先去访问里面的childrennode ifnode.name=='add': node.name='sub' args_num=len(node.args.args) args_num=len(node.args.args) args=tuple([arg.argforarginnode.args.args]) print(str(args)) func_log_stmt=''.join(["print('callingfunc:%s',"%node.name,"'args:'",",%s"*args_num%args,')']) node.body.insert(0,ast.parse(func_log_stmt)) #func_log_stmt=''.join(["print'callingfunc:%s',"%node.name,"'args:'",",%s"*args_num%args]) #node.body.insert(0,ast.parse(func_log_stmt)) returnnode defvisit_Name(self,node): replace={'add':'sub','x':'a','y':'b'} re_id=replace.get(node.id,None) node.id=re_idornode.id self.generic_visit(node) returnnode defvisit_arg(self,node): self.generic_visit(node) replace={'x':'a','y':'b'} node.arg=replace[node.arg] returnnode r_node=ast.parse(func_def) transformer=CodeTransformer() r_node=transformer.visit(r_node) #print(astunparse.dump(r_node)) source=astunparse.unparse(r_node)#astunparse一般python不自带,需要conda或者pip安装 print(source) 输出: ('a','b') defsub(a,b): print('callingfunc:sub','args:',a,b) return(a-b) print(sub(3,5)) 可以看加入了一个print语句,然后将变量名字由x,y改为了a,b Keepinmindthatifthenodeyou’reoperatingonhaschildnodesyoumusteithertransformthechildnodesyourselforcallthegeneric_visit()methodforthenodefirst. Don’tusetheNodeVisitorifyouwanttoapplychangestonodesduringtraversal.Forthisaspecialvisitorexists(NodeTransformer)thatallowsmodifications. 推荐阅读更多精彩内容某哪儿机票JS解密(5)-通过AST(抽象语法树)反混淆JS这几天看了一篇大佬写的AST文章的文章,深有感触,今天正好拿去哪儿的JS开刀贴上大佬链接:https://mp....寄予蓝y阅读1,118评论0赞2AOP最后一块拼图|AST抽象语法树——最轻量级的AOP方法前言Aspect语法难懂?ASM字节码操作繁琐?APT难以精准找到切入点?你该试试AST了!编辑器级别...FeelsChaotic阅读4,424评论9赞29初探AST-抽象语法树中文原文:https://segmentfault.com/a/1190000016231512Javascri...hellomyshadow阅读497评论0赞0【转】AST(抽象语法树)转载:https://github.com/CodeLittlePrince/blog/issues/19前言...薯条你哪里跑阅读17,630评论7赞31抽象语法树AST的全面解析(三)AST操作抽象语法树AST的全面分析(一)抽象语法树AST的全面分析(二)前面两篇文章写到了抽象语法树的生成过程...zl_adams阅读6,788评论7赞142018-07-18先发影响力推荐指数:6.0书籍主旨关键词:特权、焦点、注意力、语言联想、情景联想观点:1.统计学现在叫数据分析,社会...Jenaral阅读4,724评论0赞5汽车追尾以后昨天,在回家的路上,坐在车里悠哉悠哉地看着三毛的《撒哈拉沙漠的故事》,我被里面的内容深深吸引住了,尽管上学时...夜阑晓语阅读2,699评论2赞8三槐堂书画院推荐:印玲师父佛教书画,敬请转发,共种福田弘扬佛法三槐堂书画院阅读1,577评论0赞4被自己以前的脑洞震惊到了!一月四号的大沙有个想法。

从昨晚到现在就一直围绕在脑子里。

或许深受那些小说的影响,或许真的就是我自己脑子或者精神么有...一個人的大沙阅读2,360评论3赞3记梦记梦前记他回国了,而事实上他其实从未来过。

我不知道我们是如何交流的,但在梦里没有语言障碍。

我时而是第三视角看着...江挽心阅读456评论2赞0抽奖赞1赞赞赏更多好文



請為這篇文章評分?