Python AST模块

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进行了分析。

Camera课程

Python教程

Java教程

Web教程

数据库教程

图形图像教程

办公软件教程

Linux教程

计算机教程

大数据教程

开发工具教程