如果想要動手嘗試一下,就需要安裝一下這個 python 視覺化函式庫。
Python 的 ast 函式庫有一個 parse 方法,可以把傳入的內容,解析成一個 AST。然後我們使用 ast.dump
將其匯出並列印。
注意:indent
這個參數是 Python 3.9 以後才有的,如果版本低的話,可以去掉,只會影響最後輸出的格式。
好了,就是這麼簡單。我們已經做到了,因為這個函式庫的功能很強大,但這裡只是用到一點點而已。其實這裡已經可以看出基本的結構了,不過我的目的是生成這棵樹的 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__] } ] } # 这里我只处理 加减乘除和数字类型的运行 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)
運行結果:
我這裡會輸出兩個東西,一個是AST 的dump;另一個是AST 的JSON 表示(邏輯結構的JSON 表示,不是物件的JSON 表示)。
把列印出來的 JSON 字串複製進文件,命名為 data.json
。我感覺直接輸出到控制台蠻有意思的,我喜歡直接看到它的結果。
執行以下指令:pytm-cli -d TB -i data.json -o demo.html
在瀏覽器開啟demo.html
即可看到效果了。
上面這種遍歷方法雖然便於理解,但是難以擴展。 AST 通常是透過 訪客模式 進行遍歷的,而且 ast 庫也提供了幾種遍歷方法。
因為這裡只需要遍歷來產生 JSON,所以不需要修改AST本身,所以我們只看下面這兩種。顯然第一種是不能用的,原因已經被藍色標記出來了。它自己說瞭如果你不關心上下文,因為生成 JSON 實際上是需要關注這個的。所以,我選擇下面的 ast.NodeVisitor
。使用它也很簡單,繼承這個類,然後對不同的節點寫不同的處理邏輯就行了(這樣把不同節點的邏輯分開了,降低了程式碼的耦合性)。
""" 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
開始的,這個優雅點的版本,我就把Module
節點也加入進去了。
以上是python的簡單四則運算語法樹視覺化怎麼實現的詳細內容。更多資訊請關注PHP中文網其他相關文章!