python的簡單四則運(yùn)算語法樹可視化
簡單四則運(yùn)算語法樹可視化
前幾天有一篇博客是關(guān)于四則運(yùn)算和二叉樹的,我是把四則運(yùn)算用二叉樹寫出來(我是用的 JSON 的形式來存儲(chǔ)和表達(dá)的),并計(jì)算最終的結(jié)果。最近,也在繼續(xù)這個(gè)方面的東西,不過遇到一些問題。所以想著先做一些簡單是事情,也許會(huì)更好吧。這篇博客的內(nèi)容也很簡單,就是給定一個(gè)四則運(yùn)算的表達(dá)式,畫出它的語法樹。如果了解過這方面知識的人,應(yīng)該都能大致畫出來,不過其實(shí)也是挺費(fèi)事的。如果包含了多層括號嵌套,也注定了是一個(gè)費(fèi)時(shí)費(fèi)力的事情。這種機(jī)械的事情,讓程序自己來做是最好不過的了。所以,接下來我會(huì)用到 Python 自帶的 ast 庫來解析四則運(yùn)算(殺雞用牛刀,哈哈)。
如果想要?jiǎng)邮謬L試一下,需要安裝一下這個(gè) python 可視化庫。
解析 AST
Python 的 ast 庫有一個(gè) parse 方法,可以把傳入的內(nèi)容,解析成一個(gè) AST。然后我們使用 ast.dump
將其導(dǎo)出并打印。
注意:indent
這個(gè)參數(shù)是 Python 3.9 以后才有的,如果版本低的話,可以去掉,只會(huì)影響最后輸出的格式。
好了,就是這么簡單。我們已經(jīng)做到了,因?yàn)檫@個(gè)庫的功能很強(qiáng)大,但是這里只是用到一點(diǎn)點(diǎn)而已。其實(shí)這里已經(jīng)可以看出基本的結(jié)構(gòu)了,不過我的目的是生成這棵樹的 JSON 表示。我想要使用上面的 Python 繪圖庫把它繪制出來,它所支持的輸入是 JSON,并且它的格式為:
{ "name": "A", "children": [ "name": "B", "children": [] ] }
粗糙的遍歷方法
""" Python's AST 利用 Python 的 ast 模塊來解析表達(dá)式(簡單的二元運(yùn)算), 然后通過遍歷語法樹來生成 JSON 表示,再使用 PYthon 的庫來 將其可視化。這個(gè)程序的目的是為了驗(yàn)證自己寫的簡易解析器是否正確。 """ import ast import json # 操作類型和操作符映射的字典 OPERATORS = { ast.Add: "+", ast.Sub: "-", ast.Mult: "*", ast.Div: "/" } def generate(tree: ast.Module): """ generate expression AST's representation of JSON """ if not tree: raise Exception("Emtpy AST tree!") if tree.__class__ == ast.Module: print(json.dumps({ "name": "Expr", "children": [DFS(tree.body[0].value)] # type: ignore }, indent=4)) def DFS(node): """ DFS AST """ if not node: return {} if node.__class__ == ast.BinOp: return { "name": "BinOp", "children": [ { "name": "left", "children": [ DFS(node.left) ] }, DFS(node.op), { "name": "left", "children": [ DFS(node.right) ] } ] } if node.__class__ == ast.Constant: return { "name": "NUMBER", "children": [ { "name": str(node.value) # Python 的繪圖庫,必須是字符串才能正常顯示 } ] } if node.__class__ in [ast.Add, ast.Sub, ast.Mult, ast.Div]: return { "name": "Op", "children": [ { "name": OPERATORS[node.__class__] } ] } # 這里我只處理 加減乘除和數(shù)字類型的運(yùn)行 raise Exception("There is not support extra type.") if __name__ == "__main__": ast_tree = ast.parse("1+2+3+4+5") print(ast.dump(ast_tree, indent=4)) generate(ast_tree)
運(yùn)行結(jié)果:
我這里會(huì)輸出兩個(gè)東西,一個(gè)是 AST 的 dump;另一個(gè)是 AST 的 JSON 表示(邏輯結(jié)構(gòu)的 JSON 表示,不是對象的 JSON 表示)。
渲染顯示
把打印出來的 JSON 字符串復(fù)制進(jìn)文件,命名為 data.json
。我感覺直接輸出到控制臺蠻有意思的,我喜歡直接看到它的結(jié)果。
執(zhí)行如下命令:pytm-cli -d TB -i data.json -o demo.html
在瀏覽器打開 demo.html
即可看到效果了。
主流的遍歷方法
上面這種遍歷方法雖然便于理解,但是難以擴(kuò)展。AST 通常是通過 訪問者模式 進(jìn)行遍歷的,而且 ast 庫也提供了幾種遍歷方法。
因?yàn)檫@里只需要遍歷來生成 JSON,并不需要修改AST本身,所以我們只看下面這兩種即可。顯然第一種是不能用的,原因已經(jīng)用藍(lán)色標(biāo)記出來了。它自己說了如果你不關(guān)心上下文,因?yàn)樯?JSON 實(shí)際上是需要關(guān)注這個(gè)的。所以,我選擇下面的 ast.NodeVisitor
。使用它也很簡單,繼承這個(gè)類,然后對不同的節(jié)點(diǎn)寫不同的處理邏輯就行了(這樣把不同節(jié)點(diǎn)的邏輯分開了,降低了代碼的耦合性)。
完整代碼
""" Python's AST 利用 Python 的 ast 模塊來解析表達(dá)式(簡單的二元運(yùn)算), 然后通過遍歷語法樹來生成 JSON 表示,再使用 PYthon 的庫來 將其可視化。這個(gè)程序的目的是為了驗(yàn)證自己寫的簡易解析器是否正確。 """ import ast import json # 操作類型和操作符映射的字典 OPERATORS = { ast.Add: "+", ast.Sub: "-", ast.Mult: "*", ast.Div: "/" } class JSONVisitor(ast.NodeVisitor): """ JSON visitor: Traversal AST and generate JSON representation """ def visit_Module(self, node): module = { "name": "Module", "children": [] } for sub_node in node.body: module["children"].append(self.visit(sub_node)) return module def visit_Expr(self, node): return { "name": "Expr", "children": [ self.visit(node.value) ] } def visit_BinOp(self, node): return { "name": "BinOp", "children": [ { "name": "left", "children": [ self.visit(node.left) ] }, self.visit(node.op), { "name": "right", "children": [ self.visit(node.right) ] } ] } def visit_Constant(self, node): return { "name": "NUMBER", "children": [{ "name": str(node.value) # # Python 的繪圖庫,必須是字符串才能正常顯示 }] } def visit_Add(self, node): return self.__visit(node) def visit_Sub(self, node): return self.__visit(node) def visit_Mult(self, node): return self.__visit(node) def visit_Div(self, node): return self.__visit(node) def __visit(self, node): return { "name": "Op", "children": [{ "name": OPERATORS[node.__class__] }] } if __name__ == "__main__": ast_tree = ast.parse("1+2+3+4+5") visitor = JSONVisitor() json_str = visitor.visit(ast_tree) print(json.dumps(json_str, indent=4))
前面那個(gè)粗糙版本是直接從 Expr
開始的,這個(gè)優(yōu)雅點(diǎn)的版本,我就把 Module
節(jié)點(diǎn)也添加進(jìn)去了。
說明
這個(gè)東西挺有趣的,如果想要了解這些知識的話,可以來看看這個(gè)圖示。這也相當(dāng)于給自己寫了一個(gè)小工具了,我之后還會(huì)用到它來對比我自己生成的 AST,如果不一樣,那一定是我寫的有問題了。
到此這篇關(guān)于python的簡單四則運(yùn)算語法樹可視化的文章就介紹到這了,更多相關(guān)python四則語法樹可視化內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python使用wxPython實(shí)現(xiàn)計(jì)算器
這篇文章主要為大家詳細(xì)介紹了Python使用wxPython實(shí)現(xiàn)計(jì)算器,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01Python中使用多進(jìn)程來實(shí)現(xiàn)并行處理的方法小結(jié)
本篇文章主要介紹了Python中使用多進(jìn)程來實(shí)現(xiàn)并行處理的方法小結(jié),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08Python 正則表達(dá)式實(shí)現(xiàn)計(jì)算器功能
本篇文章主要介紹了Python 正則表達(dá)式實(shí)現(xiàn)計(jì)算器功能的示例。具有很好的參考價(jià)值。下面跟著小編一起來看下吧2017-04-04使用python將mysql數(shù)據(jù)庫的數(shù)據(jù)轉(zhuǎn)換為json數(shù)據(jù)的方法
這篇文章主要介紹了使用python將mysql數(shù)據(jù)庫的數(shù)據(jù)轉(zhuǎn)換為json數(shù)據(jù)的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-07-07python不支持i++運(yùn)算原理實(shí)例解析
這篇文章主要為大家介紹了python不支持 i++運(yùn)算原理實(shí)例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-02-02Python實(shí)現(xiàn)的ftp服務(wù)器功能詳解【附源碼下載】
這篇文章主要介紹了Python實(shí)現(xiàn)的ftp服務(wù)器功能,結(jié)合實(shí)例形式分析了Python構(gòu)建ftp服務(wù)器功能的相關(guān)設(shè)置、實(shí)現(xiàn)技巧與操作注意事項(xiàng),并附帶源碼供讀者下載參考,需要的朋友可以參考下2019-06-06Python讀取圖片屬性信息的實(shí)現(xiàn)方法
這篇文章介紹了利用Python讀取圖片屬性信息的方法,讀取的內(nèi)容包括GPS 信息、圖片分辨率、圖片像素、設(shè)備商、拍攝設(shè)備等,有需要的朋友們可以參考借鑒。2016-09-09