Python AST模块
在本教程中,我们将学习如何使用AST来理解代码。
AST模块是什么
AST代表 抽象语法树, 是Python编程语言的一个强大工具。它允许我们与Python代码本身进行交互并进行修改。
你曾经想过Python代码是如何运行的吗?背后是否有奇迹?
对于那些不知道的人来说,Python解释器负责运行Python代码。它遵循预先编写的指令,将Python代码转换为机器可以运行的指令。
下面是将Python代码转换为机器代码的过程。
- 当我们运行代码时,代码被解析为称为标记的较小块。这些标记由预定义的指令创建,这些指令应该进行不同的处理。例如 – 关键字 else 与数字值(如
- 标记被存储在转换成抽象语法树(AST)的列表中。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 0x0000010B889A6AC0>
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)))
创建多行AST
在之前的示例中,我们已经看到了单行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=[])
NodeTransformer 和 NodeVisitor
NodeTransformer 类被用于获取不同类型的节点,并根据我们的需求进行修改。ast 模块还提供了 NodeVisitor 类,它可以帮助我们在遍历树时调用 visit 函数。这样我们可以更灵活地控制节点,让我们通过下面的示例来理解。
示例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模块。然后我们定义了继承NodeVisitor类的Visitor类。每次找到字符串节点时,它都会添加相应的前缀。
当我们直接运行源代码时,我们也可以使用该模块。让我们来理解下面的示例。
示例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 的抽象语法树。然后, parse() 函数处理了所有的标记,遵循了所有的语言规则,并构建了一个树形数据结构,其中包含了许多有用的信息。
树就是一组节点的集合,其中一个树变量被引用为“根”节点。因此,我们可以访问树中的每个节点并执行操作。但首先,我们需要访问每个节点并处理数据。
分析抽象语法树
当我们得到树时,现在分析器遵循访问者模式。使用NodeVisitor类,我们可以跟踪Python中的任何节点。我们需要实现一个方法 **visit_
示例
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代码方面非常有帮助。然而,AST仍然不够用户友好,但比字节码表示更容易理解。
何时使用Python AST模块
抽象语法树对于代码覆盖工具非常有帮助。它解析源代码并查找代码中可能的缺陷和错误。它还可以用于:
- 作为自定义Python解释器使用。
- 用于分析静态代码。
- 使IDE智能化,即所谓的IntelliSense。
结论
我们学习了Python中的ast模块,它负责运行Python代码。然后,我们从Python代码构建了AST树,并使用 NodeVisitor 类对AST进行了分析。