在本教程中,我們將學習如何使用 AST 來理解代碼。
什麼是 ast
模塊?
AST 代表抽象語法樹,,它是 Python 編程語言的有力工具。它允許我們與 Python 代碼本身進行交互,並可以對其進行修改。
你有沒有想過 Python 代碼是如何運行的?背後有魔法嗎?
對於不知道的人,Python 解釋器負責運行 Python 代碼。它遵循預先編寫的指令,將 Python 代碼翻譯成機器可以運行的指令。
下面是將 Python 代碼轉換為機器代碼的過程。
- 當我們運行代碼時,代碼被解析成稱為標記的更小的塊。這些令牌是由應該區別對待的預定義指令創建的。例如,關鍵字或是不同於數值的關鍵字,如
- 令牌存儲在轉換後的列表中,以構建抽象語法樹。AST 是基於 Python 語言語法鏈接在一起的兩個或多個節點的集合。
- 編譯器可以從 AST 產生稱為二進位代碼的低級指令。這段代碼非常通用,因此計算機可以輕鬆運行。
- 當解釋器得到類似位元組碼的指令時,解釋器現在可以運行代碼了。位元組碼負責在操作系統中調用函數,最終將與 CPU 和內存交互運行程序。
上面的描述是解釋器如何使用 AST 運行 Python 代碼的粗略草圖。
代碼編譯模式
有三種模式可以編譯代碼。它們如下所示。
- exec – 該模式用於執行正常的 Python 代碼。
- eval – 該模式用於評估 Python 的表達式,評估後會返回結果。
- single – 該模式作為 Python shell 工作,一次執行一條語句。
執行 Python 代碼
使用 ast
模塊,我們可以運行 Python 代碼。讓我們理解下面的例子。
示例-
import ast
code = ast.parse("print('Hello Learner ! Welcome to JavaTpoint')")
print(code)
exec(compile(code, filename="", mode="exec"))
輸出:
<_ast.module object="" at="">
Hello Learner! Welcome to JavaTpoint
評估 Python 表達式
ast
模塊允許我們評估 Python 表達式,並從表達式中返回結果。讓我們理解下面的例子。
示例-
import ast
expression = '6 + 8'
code = ast.parse(expression, mode='eval')
print(eval(compile(code, '', mode='eval')))
print(ast.dump(code))
輸出:
14
Expression(body=BinOp(left=Constant(value=6, kind=None), op=Add(), right=Constant(value=8, kind=None)))
創建多行 ASTs
在前面的例子中,我們已經看到了單行 AST 以及如何轉儲它們。現在,我們將學習如何創建多行 AST。首先,讓我們理解下面的例子。
示例-
import ast
tree_ast = ast.parse('''
subjects = ['computer science', 'alorithm']
name = 'Ricky'
for sub in subjects:
print('{} learn {}'.format(name, subjects))
''')
print(ast.dump(tree_ast))
輸出:
Module(body=[Assign(targets=[Name(id='subjects', ctx=Store())], value=List(elts=[Constant(value='computer science', kind=None), Constant(value='alorithm', kind=None)], ctx=Load()), type_comment=None), Assign(targets=[Name(id='name', ctx=Store())], value=Constant(value='Ricky', kind=None), type_comment=None), For(target=Name(id='fruit', ctx=Store()), iter=Name(id='fruits', ctx=Load()), body=[Expr(value=Call(func=Name(id='print', ctx=Load()), args=[Call(func=Attribute(value=Constant(value='{} learn {}', kind=None), attr='format', ctx=Load()), args=[Name(id='name', ctx=Load()), Name(id='subjects', ctx=Load())], keywords=[])], keywords=[]))], orelse=[], type_comment=None)], type_ignores=[])
節點轉換器和節點訪問者
節點變壓器類用於根據我們的要求採取不同的類型和修改。ast 模塊還提供了 NodeVisitor 類,幫助我們在每次遍歷樹的時候調用訪問函數。為了更好地控制節點,我們來理解下面的例子。
示例- 1
import ast
class Visitor(ast.NodeVisitor):
def visit_Str(self, node):
print('String Node: "' + node.s + '"')
class MyTransformer(ast.NodeTransformer):
def visit_Str(self, node):
return ast.Str('str: ' + node.s)
parsed = ast.parse("print('Hello World')")
MyTransformer().visit(parsed)
Visitor().visit(parsed)
輸出:
Welcome to the Javatpoint
解釋-
在上面的代碼中,我們已經導入了解析代碼的 ast 模塊。然後我們定義了繼承了節點訪問者類的訪問者類。每次找到字元串節點;它通過添加前綴而被轉換。
當我們直接運行源代碼時,我們也可以使用該模塊。讓我們理解下面的例子。
示例- 2:
import ast
from pprint import pprint
def main():
with open("ast_module.py", "r") as source:
ast_tree = ast.parse(source.read())
analysis = Analyzer()
analysis.visit(ast_tree)
analysis.report()
class Analyzer(ast.NodeVisitor):
def __init__(self):
self.stats = {"import": [], "from": []}
def node_visit(self, node):
for alias in node.names:
self.stats["import"].append(alias.name)
self.generic_visit(node)
def node_visitFrom(self, node):
for alias in node.names:
self.stats["from"].append(alias.name)
self.generic_visit(node)
def report(self):
pprint(self.stats)
if __name__ == "__main__":
輸出:
{'from': ['pprint'], 'import': ['ast']}
解釋-
上面的代碼將 Python 文件轉換為抽象語法樹。然後我們分析樹以獲得有用的信息。
我們已經在讀取模式下打開了一個 Python 文件,然後創建了一個名為 ast_tree 的 AST。然後, parse() 函數處理所有的標記,遵循所有的語言規則,並構建一個包含大量有用信息的樹形數據結構。
樹只不過是節點的集合,其中一個樹變數引用了「根」節點。因此,我們可以訪問樹中的每個節點並執行操作。但是,首先,我們訪問每個節點並處理數據。
分析 AST
一旦我們得到樹,現在分析器遵循訪問者模式。使用節點訪問者類,我們可以跟蹤 Python 中的任何節點。我們需要實現一個方法訪問 _ <節點類型> 來訪問特定的節點類型。在前面的例子中,我們使用了下面的腳本。
示例-
class Analyzer(ast.NodeVisitor):
def node_visit(self, node):
for alias in node.names:
self.stats["import"].append(alias.name)
self.generic_visit(node)
def node_visitFrom(self, node):
for alias in node.names:
self.stats["from"].append(alias.name)
self.generic_visit(node)
代碼接受模塊的名稱,並將其存儲在統計列表中。藉助 NodeVisitor 類,我們可以對樹進行分析。
analyzer = Analyzer()
analyzer.visit(tree)
visit()方法的工作原理與 visit_ <節點類型> 方法相同。
使用 AST 作為分析工具
Python 代碼變成位元組碼後,人類無法讀取。但它使解釋器變得更快,這意味著位元組碼是為機器設計的,而不是為人類設計的。
AST 由足夠多的結構化信息組成,這使得它們有助於學習 Python 代碼。然而,ASTs 仍然不是用戶友好的,但是它們比位元組代碼表示更容易理解。
什麼時候用 Python ast
模塊?
抽象語法樹對代碼覆蓋工具很有幫助。它解析源代碼,發現代碼中可能存在的缺陷和錯誤。它也可以用在-
- 它被用作定製的 Python 解釋器。
- 它用於分析靜態代碼。
- 它使 IDEs 變得智能,這就是所謂的智能感知。
結論
我們已經了解了 Python 中負責運行 Python 代碼的 ast 模塊。然後,我們從 Python 代碼中構建了 AST 樹,並使用節點訪問者類對 AST 進行了分析。
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/296254.html