Python进阶之-ast使用详解

✨前言:

🌟什么是ast?

ast模块在Python中用于将源码转换成抽象语法树(Abstract Syntax Trees,AST)。通过AST,我们可以读取、修改、分析Python代码。本质上,它将源码转化为树形结构,节点代表语法构造,如表达式、语句等。这对于编写代码分析、优化工具或自动代码生成等任务非常有用。
抽象语法树(AST)组成部分
Expression: 在Python的AST中,Expression节点表示一个表达式。这里,它是整个AST的根节点。Expression节点有一个body属性,用于存储表达式的主体。body=BinOp(…): body属性包含一个BinOp节点,表示一个二元操作(binary operation)。二元操作就是涉及两个操作数的运算,比如加法、减法等。left=Constant(value=2): 在BinOp节点中,left属性表示左侧的操作数。

🌟基本使用

首先,使用import ast命令导入ast模块。

⭐️例1:解析表达式
#!/usr/bin/env python
# coding=utf-8
"""
# @Time    : 2024/5/4
# @Author  : Summer
# @File    : test
# @describe:
"""
import ast

expression = "(1 + 2) * 3"
tree = ast.parse(expression, mode='eval')

print(ast.dump(tree, indent=4))
# Expression(body=BinOp(left=BinOp(left=Constant(value=1, kind=None), op=Add(),
# right=Constant(value=2, kind=None)), op=Mult(), right=Constant(value=3, kind=None)))

该示例中,先通过ast.parse函数解析字符串表达式。mode='eval’表示这是一个表达式。然后利用ast.dump函数打印出该表达式的树形结构,indent=4参数使输出更加易于阅读。

Expression:表示这是一个表达式节点。在AST种,Expression用于包装一个表达式,这里它是根节点。
body:这是Expression节点的主体,它包含了真正的运算逻辑。
BinOp:代表二元操作的节点,即涉及两个操作数的运算。在这个例子中,有两层BinOp。
第一层BinOp(外层)代表乘法操作(Mult()),它的两个操作数分别是: left=BinOp()
(内层):这个代表加法操作(Add())。具体包含: left=Constant(value=1,
kind=None):这是一个常量节点,表示整数1。 right=Constant(value=2,
kind=None):这也是一个常量节点,表示整数2。 这表明内层BinOp表示的表达式是1 + 2。 第一层BinOp的另一个操作数是:
right=Constant(value=3, kind=None):这是一个表示整数3的常量节点。
op:这个属性表示操作类型,Add()是加法操作,Mult()是乘法操作。 因此,这整个Expression节点表示的表达式是:(1 +
2) * 3。 kind=None:在Constant节点中,kind属性表示常量的字面种类。

⚠️注意:indent=4参数是python3.9版本之后引入的,需要注意自己的python版本。

⭐️例2:遍历AST节点

要访问AST的每个节点,最直接的方法是使用ast.NodeVisitor类。下面我们实现一个简单的访问者,计算AST树中出现的所有Num节点的总和:

#!/usr/bin/env python
# coding=utf-8
"""
# @Time    : 2024/5/4
# @Author  : Summer
# @File    : test
# @describe:
"""
import ast


class SumNumbers(ast.NodeVisitor):
    def __init__(self):
        self.total = 0

    def visit_Num(self, node):
        self.total += node.n
        self.generic_visit(node)

expression = "1 + 2 + (3 * 4)"
tree = ast.parse(expression, mode='eval')
visitor = SumNumbers()
visitor.visit(tree)

print(visitor.total)  # 10

上例中,SumNumbers类继承自ast.NodeVisitor,通过定义visit_方法来处理特定类型的节点。这里,我们处理Num节点,即数值。
👉为什么输出是10?
对于表达式 “1 + 2 + (3 * 4)”,简化的AST遍历如下:
数值 1(Constant节点)
数值 2(Constant节点)
数值 3(Constant节点)
数值 4(Constant节点)
根据此逻辑,总结如下:

1出现1次。
2出现1次。
3出现1次(作为乘法操作的一部分)。
4出现1次(作为乘法操作的一部分)。
累加这些数值得到的total是10,实际上,AST保持了源码的结构,对于(3 * 4),它会以3和4两个独立的Constant节点存在于树中,同时由BinOp节点表示这个乘法操作。

⭐️例3:修改AST节点

若要修改AST,可以使用ast.NodeTransformer类。以下例子把所有的数字乘以2:

#!/usr/bin/env python
# coding=utf-8
"""
# @Time    : 2024/5/4
# @Author  : Summer
# @File    : test
# @describe:
"""
import ast


class DoubleNumbers(ast.NodeTransformer):
    def visit_Num(self, node):
        return ast.copy_location(ast.Num(n=node.n * 2), node)

expression = "1 + 2"
tree = ast.parse(expression, mode='eval')
transformer = DoubleNumbers()
new_tree = transformer.visit(tree)

print(ast.dump(new_tree)) 

这个例子通过继承ast.NodeTransformer并重写visit_Num方法,来修改数值节点。ast.copy_location帮助保持原节点的源代码位置信息。

⭐️ast.Attribute

ast.Attribute 类型在 Python 的抽象语法树(AST)中用于表示对属性的访问,例如在访问对象的属性或方法时。举一个简单的例子,当你有一个像 object.attribute 这样的代码时,可以通过 ast 模块来解析和理解这种结构。

先看一个具体的代码示例,然后解析它:

#!/usr/bin/env python
# coding=utf-8
"""
# @Time    : 2024/5/4
# @Author  : Summer
# @File    : test
# @describe:
"""

import ast

# 假设我们有如下代码,我们想要解析它
code = """
class MyClass:
    def method(self):
        pass

obj = MyClass()
obj.method()
"""

# 使用 ast.parse 将代码字符串解析为 AST
parsed_code = ast.parse(code)


# 定义一个访问者类,该类继承自 ast.NodeVisitor
class MyVisitor(ast.NodeVisitor):
    def visit_Attribute(self, node):
        print(f"Found an attribute access: {node.attr}")
        self.generic_visit(node)  # 继续访问节点的子节点


# 实例化我们的访问者,并访问解析得到的 AST
visitor = MyVisitor()
visitor.visit(parsed_code)  # Found an attribute access: method

在这个例子中,我们定义了一个名为 MyClass 的类和一个该类的实例 obj。随后,我们通过 obj.method() 调用类的一个方法。我们的目标是通过访问 AST 来找到这种对属性(在这个案例中是方法)的访问。这里,当实例化 MyVisitor 类并调用它的 visit 方法时,它会遍历整个 AST。每当遇到 ast.Attribute 类型的节点时,visit_Attribute 方法将被调用,打印出我们正在访问的属性的名字。输出结果为 Found an attribute access: method 这表明,我们成功地识别了代码中对属性(或方法)method 的访问。

⭐️ ast.Constant

ast.Constant 是 Python 抽象语法树(AST)中表示字面量值的节点类型。这包括数值字面量(如整数、浮点数)、字符串字面量、布尔值(True 和 False)以及 None。下面通过一个例子介绍如何使用 ast 模块来解析这些常量,并展示如何访问它们。
假设我们有一段包含字面量值的 Python 代码,我们希望解析这段代码并识别出所有的常量值。

#!/usr/bin/env python
# coding=utf-8
"""
# @Time    : 2024/5/4
# @Author  : Summer
# @File    : test
# @describe:
"""

import ast

# 定义一个包含各种常量的代码字符串
code = """
x = 10
y = 3.14
name = "Python"
flag = True
nothing = None
"""

# 使用 ast.parse 将代码字符串解析为 AST
parsed_code = ast.parse(code)

# 定义一个访问者类,该类继承自 ast.NodeVisitor
class FindConstants(ast.NodeVisitor):
    def visit_Constant(self, node):
        print(f"Found a constant: {node.value}")
        self.generic_visit(node)  # 继续访问节点的子节点

# 实例化我们的访问者,并访问解析得到的 AST
finder = FindConstants()
finder.visit(parsed_code)

在这个例子中,我们首先定义了一个包含不同类型常量的代码字符串,随后通过 ast.parse 函数将其解析为 AST。然后,我们定义了一个 FindConstants 类,它是 ast.NodeVisitor 的子类,用于遍历 AST。对于每一个遇到的 ast.Constant 节点,我们将其值打印出来。
输出:

Found a constant: 10
Found a constant: 3.14
Found a constant: Python
Found a constant: True
Found a constant: None

这表示我们成功识别并访问了代码中的所有常量。通过这种方式,使用 ast 模块可以使我们能够对 Python 代码进行静态分析,例如识别代码中的硬编码值、分析代码的结构等。ast.NodeVisitor 提供了一种强大的机制,允许我们通过重写其方法来自定义对不同类型节点的访问行为,从而实现对 AST 的遍历和操作。

🌟ast常见用途

静态代码分析:分析代码结构,检查编码规范或查找潜在的bug。
代码优化:修改AST,进行诸如常量传播、死码删除等简单优化。
代码生成:根据特定的逻辑生成新的Python代码片段。
注意事项
使用ast时要小心执行任意的Python代码,特别是使用ast.literal_eval时,因为它比eval安全,但仍需谨慎对待输入源。
修改AST需要理解Python的语法结构,否则可能产生语法错误的代码。
ast模块是Python语言强大的特性之一。它不仅允许代码自省和动态修改,还可以用于构建复杂的编程工具和框架。

  • 40
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夏天Aileft

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值