python的簡單四則運算語法樹可視化
簡單四則運算語法樹可視化
前幾天有一篇博客是關(guān)于四則運算和二叉樹的,我是把四則運算用二叉樹寫出來(我是用的 JSON 的形式來存儲和表達的),并計算最終的結(jié)果。最近,也在繼續(xù)這個方面的東西,不過遇到一些問題。所以想著先做一些簡單是事情,也許會更好吧。這篇博客的內(nèi)容也很簡單,就是給定一個四則運算的表達式,畫出它的語法樹。如果了解過這方面知識的人,應(yīng)該都能大致畫出來,不過其實也是挺費事的。如果包含了多層括號嵌套,也注定了是一個費時費力的事情。這種機械的事情,讓程序自己來做是最好不過的了。所以,接下來我會用到 Python 自帶的 ast 庫來解析四則運算(殺雞用牛刀,哈哈)。
如果想要動手嘗試一下,需要安裝一下這個 python 可視化庫。

解析 AST
Python 的 ast 庫有一個 parse 方法,可以把傳入的內(nèi)容,解析成一個 AST。然后我們使用 ast.dump 將其導(dǎo)出并打印。
注意:indent 這個參數(shù)是 Python 3.9 以后才有的,如果版本低的話,可以去掉,只會影響最后輸出的格式。

好了,就是這么簡單。我們已經(jīng)做到了,因為這個庫的功能很強大,但是這里只是用到一點點而已。其實這里已經(jīng)可以看出基本的結(jié)構(gòu)了,不過我的目的是生成這棵樹的 JSON 表示。我想要使用上面的 Python 繪圖庫把它繪制出來,它所支持的輸入是 JSON,并且它的格式為:
{
"name": "A",
"children": [
"name": "B",
"children": []
]
}
粗糙的遍歷方法
"""
Python's AST
利用 Python 的 ast 模塊來解析表達式(簡單的二元運算),
然后通過遍歷語法樹來生成 JSON 表示,再使用 PYthon 的庫來
將其可視化。這個程序的目的是為了驗證自己寫的簡易解析器是否正確。
"""
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ù)字類型的運行
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)
運行結(jié)果:
我這里會輸出兩個東西,一個是 AST 的 dump;另一個是 AST 的 JSON 表示(邏輯結(jié)構(gòu)的 JSON 表示,不是對象的 JSON 表示)。

渲染顯示
把打印出來的 JSON 字符串復(fù)制進文件,命名為 data.json。我感覺直接輸出到控制臺蠻有意思的,我喜歡直接看到它的結(jié)果。
執(zhí)行如下命令:pytm-cli -d TB -i data.json -o demo.html
在瀏覽器打開 demo.html 即可看到效果了。

主流的遍歷方法
上面這種遍歷方法雖然便于理解,但是難以擴展。AST 通常是通過 訪問者模式 進行遍歷的,而且 ast 庫也提供了幾種遍歷方法。
因為這里只需要遍歷來生成 JSON,并不需要修改AST本身,所以我們只看下面這兩種即可。顯然第一種是不能用的,原因已經(jīng)用藍色標(biāo)記出來了。它自己說了如果你不關(guān)心上下文,因為生成 JSON 實際上是需要關(guān)注這個的。所以,我選擇下面的 ast.NodeVisitor。使用它也很簡單,繼承這個類,然后對不同的節(jié)點寫不同的處理邏輯就行了(這樣把不同節(jié)點的邏輯分開了,降低了代碼的耦合性)。

完整代碼
"""
Python's AST
利用 Python 的 ast 模塊來解析表達式(簡單的二元運算),
然后通過遍歷語法樹來生成 JSON 表示,再使用 PYthon 的庫來
將其可視化。這個程序的目的是為了驗證自己寫的簡易解析器是否正確。
"""
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))
前面那個粗糙版本是直接從 Expr 開始的,這個優(yōu)雅點的版本,我就把 Module 節(jié)點也添加進去了。

說明
這個東西挺有趣的,如果想要了解這些知識的話,可以來看看這個圖示。這也相當(dāng)于給自己寫了一個小工具了,我之后還會用到它來對比我自己生成的 AST,如果不一樣,那一定是我寫的有問題了。
到此這篇關(guān)于python的簡單四則運算語法樹可視化的文章就介紹到這了,更多相關(guān)python四則語法樹可視化內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python+SeaTable實現(xiàn)計算兩個日期間的工作日天數(shù)
在實際的項目管理、任務(wù)管理、工作計劃等場景中,某些時間段會涉及雙休日、法定節(jié)假日,甚至還有公司自定義的工作時間安排,所以就需要計算出兩個日期間的實際工作日天數(shù)。本文用Python+SeaTable實現(xiàn)這一需求,需要的可以參考一下2022-07-07
Python使用xpath對解析內(nèi)容進行數(shù)據(jù)提取
XPath 使用路徑表達式來選取HTML/ XML 文檔中的節(jié)點或節(jié)點集,節(jié)點是通過沿著路徑 (path) 或者步 (steps) 來選取的,本文將給大家介紹Python使用xpath對解析內(nèi)容進行數(shù)據(jù)提取的方法,需要的朋友可以參考下2024-05-05
Python3.7+tkinter實現(xiàn)查詢界面功能
這篇文章主要介紹了Python3.7+tkinter實現(xiàn)查詢界面功能,本文通過實例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2019-12-12
使用Python3中的gettext模塊翻譯Python源碼以支持多語言
這篇文章主要介紹了使用Python3中的gettext模塊翻譯Python源碼以支持多語言,其中翻譯Python源碼只是作為示例以展示gettext的功能和用法,需要的朋友可以參考下2015-03-03
Pandas如何將Timestamp轉(zhuǎn)為datetime類型
這篇文章主要介紹了Pandas如何將Timestamp轉(zhuǎn)為datetime類型,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-07-07

