Python Ast介紹及應用- IT閱讀
文章推薦指數: 80 %
Ast是python原始碼到位元組碼的一種中間產物,藉助ast模組可以從語法樹的角度分析原始碼結構。
此外,我們不僅可以修改和執行語法樹,還可以將Source生成的 ...
程式人生>>PythonAst介紹及應用
PythonAst介紹及應用
阿新••發佈:2018-12-15
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
延伸文章資訊
- 1ast — Abstract Syntax Trees — Python 3.10.4 documentation
The ast module helps Python applications to process trees of the Python abstract syntax grammar. ...
- 2Python 函式eval 與ast.literal_eval 的區別
主題: Python. 一分鐘學個小知識. eval 與 ast.literal_eval 都可以將字串還原成它能夠轉化成的資料型別,例如 >>> from ast import literal...
- 3Python ast.parse方法代碼示例- 純淨天空
Python ast.parse方法代碼示例,ast.parse用法. ... 需要導入模塊: import ast [as 別名] # 或者: from ast import parse [a...
- 4Python ast.Module方法代碼示例- 純淨天空
Python ast. ... 如果您正苦於以下問題:Python ast. ... 需要導入模塊: import ast [as 別名] # 或者: from ast import Modul...
- 5Python Ast介紹及應用- IT閱讀
Ast是python原始碼到位元組碼的一種中間產物,藉助ast模組可以從語法樹的角度分析原始碼結構。此外,我們不僅可以修改和執行語法樹,還可以將Source生成的 ...