Python-AST语法树

一、抽象语法树

1、什么是抽象语法树

在计算机科学中,抽象语法树abstract syntax tree ,AST),是源代码的抽象语法结构的树状表现形式,这里特指编程语言的源代码。AST是编译器或解释器在处理源代码时所使用的一种中间表示形式,在编译和代码生成过程中起着关键作用。

之所以说语法是「抽象」的,是因为这里的语法并不会表示出真实语法中出现的每个细节。

AST中的每个节点表示源代码中的一个语法构造,如变量声明、表达式、函数调用、控制结构等。树的根节点通常表示整个源代码文件,而子节点表示具体的语法元素及其关系。例如,一个函数声明的AST节点可能包含多个子节点,如函数名、参数列表和函数体等。

2、使用场景

python 和 Java 类似,解释器实际上分为两部分:编译器和虚拟机,先将代码编译成字节码,然后再由虚拟机执行。

在编译时,主要用途包括:

  1. 语法检查:编译器可以通过遍历AST来检查源代码中是否存在语法错误。
  2. 语义分析:编译器可以使用AST来分析源代码的语义,例如识别类型错误、作用域错误等。
  3. 优化:编译器可以通过对AST进行变换和简化,实现源代码的优化。
  4. 代码生成:编译器可以根据AST生成目标代码,例如汇编语言或机器代码。

在实际应用中,AST除了用于编译器或解释器,还被用于诸如代码重构、静态分析和代码格式化等工具和技术中。

编译器和解释器的核心:AST是编译器和解释器处理源代码的关键数据结构。在对源代码进行语法分析之后,编译器或解释器会生成AST。接下来,它们可以在AST上进行进一步的分析、优化和代码生成。这使得编译器和解释器能够更高效地处理源代码,为生成可执行程序或执行脚本提供基础。

代码分析:AST在静态代码分析和静态类型检查中起着重要作用。通过分析AST,我们可以检测代码中的潜在错误、不良实践和安全漏洞,从而提高代码质量。

代码转换和优化:编译器、解释器和其他工具(如Babel或Webpack)可以使用AST来进行代码转换和优化。这些工具可以在AST上执行各种操作,如语法转换(例如将ES6+语法转换为ES5语法)、代码压缩、代码拆分和常量传播等。这有助于提高程序的性能和兼容性。

代码生成:基于AST,编译器和解释器可以生成目标代码(例如机器代码、字节码或其他编程语言的代码)。这使得跨平台编译和运行成为可能,例如:将C++代码编译为WebAssembly,以便在Web浏览器中运行。

代码重构和编辑器支持:AST在代码重构和编辑器支持中也起着重要作用。通过分析和操作AST,我们可以实现自动化的代码重构、代码补全、语法高亮、错误检查等功能,从而提高开发者的生产力。

3、AST还能做什么

抽象语法树(AST)在编程语言处理、软件工程和开发工具中发挥着关键作用。除了前面讨论过的用途之外,AST还可以用于以下方面:

代码生成器:可以根据AST生成代码模板和脚手架工具。例如,根据类和方法定义生成REST API的客户端和服务器端实现。

语言转换:通过分析源语言的AST,然后将其转换为目标语言的AST,可以实现源代码到目标代码的转换。例如,将TypeScript代码转换为JavaScript代码。

代码覆盖率分析:通过分析AST,我们可以检测测试用例覆盖的代码范围,从而衡量测试质量和查找潜在的漏洞。

文档生成:AST可以用于提取源代码中的注释、类、方法和属性等信息,从而自动生成API文档。

代码安全性分析:通过分析AST,可以识别不安全的代码模式和潜在的安全漏洞,从而提高软件安全性。

代码审查:AST可以帮助自动化检查代码是否符合团队的编程规范和约定,从而提高代码质量和一致性。

依赖关系分析:通过分析AST,可以识别源代码中的模块、类和函数之间的依赖关系,从而理解代码结构和优化代码组织。

自动补全和代码导航:通过分析AST,集成开发环境(IDE)和代码编辑器可以提供自动补全、代码导航、变量重命名等智能功能,从而提高开发者的生产力。

语言扩展和领域特定语言(DSL):AST可以用于设计和实现领域特定语言,这些语言可以更简洁地表示特定领域的问题和解决方案。例如,通过将DSL转换为目标编程语言的AST,可以生成可执行代码。

三、Python AST

Python AST是Python源代码的抽象语法树表示形式。它是Python编译器在解析源代码时生成的一种数据结构,用于表示源代码的语法结构。

Python官方提供的CPython解释器对python源码的处理过程如下:

  • Parse source code into a parse tree (Parser/pgen.c)
  • Transform parse tree into an Abstract Syntax Tree (Python/ast.c)
  • Transform AST into a Control Flow Graph (Python/compile.c)
  • Emit bytecode based on the Control Flow Graph (Python/compile.c)

即实际python代码的处理过程如下:

  • 源代码解析 --> 语法树 --> 抽象语法树(AST) --> 控制流程图 --> 字节码

1、Python AST的基本结构

Python AST是由一系列节点(Node)组成的树形结构。每个节点代表了Python源代码中的一个语法结构,例如函数、类、变量、表达式等。每个节点都有一个类型(Type)和一些属性(Attribute),用于描述节点的语法结构和语义信息。

节点可以分类为:

  • 常量节点(Literals)
  • 变量节点(Variables)
  • 表达式节点(Expressions)
  • 声明节点(Statements)
  • 控制流节点(Control flow,if/for/while等)
  • 函数和类的定义节点(Function and class definitions)
  • 异步和等待节点(Async and await)
  • 顶层节点(Top level nodes)

以下是Python AST的一些常见节点类型:

  • Module: 代表整个Python模块。
  • FunctionDef: 代表函数定义。
  • ClassDef: 代表类定义。
  • Name: 代表变量名。
  • Constant: 代表常量。
  • BinOp: 代表二元操作符表达式。

其余的节点类型见:ast --- 抽象语法树 — Python 3.12.1 文档

2、AST模块的基本用法 

在Python中,我们可以使用ast模块提供的parse()函数将源代码解析为抽象语法树对象,并使用walk()方法遍历该对象。

除了ast.dump,有很多dump ast的第三方库,如astunparse, codegen, unparse等。这些第三方库不仅能够以更好的方式展示出ast结构,还能够将ast反向导出python source代码。

import ast
import astpretty

# 解析源代码,获得抽象语法树对象
ast_tree = ast.parse("print('Hello, world!')")
# 遍历抽象语法树对象
for node in ast.walk(ast_tree):
    # print(type(node).__name__)
    print("*"*66)
    astpretty.pprint(node)

以上代码输出内容如下:

Module(
    body=[
        Expr(
            lineno=1,
            col_offset=0,
            end_lineno=1,
            end_col_offset=22,
            value=Call(
                lineno=1,
                col_offset=0,
                end_lineno=1,
                end_col_offset=22,
                func=Name(lineno=1, col_offset=0, end_lineno=1, end_col_offset=5, id='print', ctx=Load()),
                args=[Constant(lineno=1, col_offset=6, end_lineno=1, end_col_offset=21, value='Hello, world!', kind=None)],
                keywords=[],
            ),
        ),
    ],
    type_ignores=[],
)
******************************************************************
Module(
    body=[
        Expr(
            lineno=1,
            col_offset=0,
            end_lineno=1,
            end_col_offset=22,
            value=Call(
                lineno=1,
                col_offset=0,
                end_lineno=1,
                end_col_offset=22,
                func=Name(lineno=1, col_offset=0, end_lineno=1, end_col_offset=5, id='print', ctx=Load()),
                args=[Constant(lineno=1, col_offset=6, end_lineno=1, end_col_offset=21, value='Hello, world!', kind=None)],
                keywords=[],
            ),
        ),
    ],
    type_ignores=[],
)
******************************************************************
Expr(
    lineno=1,
    col_offset=0,
    end_lineno=1,
    end_col_offset=22,
    value=Call(
        lineno=1,
        col_offset=0,
        end_lineno=1,
        end_col_offset=22,
        func=Name(lineno=1, col_offset=0, end_lineno=1, end_col_offset=5, id='print', ctx=Load()),
        args=[Constant(lineno=1, col_offset=6, end_lineno=1, end_col_offset=21, value='Hello, world!', kind=None)],
        keywords=[],
    ),
)
******************************************************************
Call(
    lineno=1,
    col_offset=0,
    end_lineno=1,
    end_col_offset=22,
    func=Name(lineno=1, col_offset=0, end_lineno=1, end_col_offset=5, id='print', ctx=Load()),
    args=[Constant(lineno=1, col_offset=6, end_lineno=1, end_col_offset=21, value='Hello, world!', kind=None)],
    keywords=[],
)
******************************************************************
Name(lineno=1, col_offset=0, end_lineno=1, end_col_offset=5, id='print', ctx=Load())
******************************************************************
Constant(lineno=1, col_offset=6, end_lineno=1, end_col_offset=21, value='Hello, world!', kind=None)
******************************************************************
Load()

 以上内容是抽象语法树中的6个节点,其中,Module表示整个模块,Expr表示一个表达式,Call表示一个函数调用。那么我们可以画出来:

import ast
import ctree
ast_tree = ast.parse("print('Hello, world!')")
ctree.ipython_show_ast(ast_tree)

从语法树中可以看出,该语句加载(Load())了名(Name())为print的函数接口(func),函数传参(args)是值为’Hello, world!’(value)的常量(Constant)。 

C语言分析:

import ast
import ctree
from ctree.transformations import PyBasicConversions

ast_tree = ast.parse("print('Hello, world!')")
# ctree.ipython_show_ast(ast_tree)
t = PyBasicConversions()
tree2 = t.visit(ast_tree)
ctree.ipython_show_ast(tree2)

 

3、使用AST模块进行代码分析

由于AST可以表示源代码的结构,因此,我们可以结合AST模块对Python代码进行语法分析、代码检查、重构等操作。

3.1. 语法分析

我们可以使用AST模块提供的NodeVisitor类来遍历代码并进行分析。NodeVisitor是一个抽象基类,它定义了许多方法,我们只需要重写需要的方法即可。

import ast

class MyVisitor(ast.NodeVisitor):
    def visit_BinOp(self, node):
        print(type(node).__name__)

# 解析源代码,获得抽象语法树对象
tree = ast.parse("a + b")

# 实例化并遍历分析器
visitor = MyVisitor()
visitor.visit(tree)

以上代码定义了一个分析器类MyVisitor,它继承自NodeVisitor并重写了visit_BinOp方法。visit_BinOp方法指在遍历二元运算符节点时被调用,因此可以在该方法中进行具体的分析。

3.2. 代码检查

AST模块提供了许多内置函数和类,可以用于检查代码是否符合某些规范。

import ast

# 检查函数名是否符合规范
def check_func_name(node):
    func_name = node.name
    if not func_name.startswith("test_"):
        print(f"函数名{func_name}不符合规范!")

# 解析源代码,获得抽象语法树对象
tree = ast.parse("def test_add(a, b):\n    return a + b\n\ndef sub(a, b):\n    return a - b")

# 遍历抽象语法树对象
for node in ast.walk(tree):
    if isinstance(node, ast.FunctionDef):
        check_func_name(node)

以上代码定义了一个检查器函数check_func_name,它判断函数名是否以"test_"开头。在遍历抽象语法树时,如果找到了函数节点,就可以调用check_func_name进行检查。

3.3. 代码重构

代码重构是指对已有代码的结构和实现进行修改以改进代码的质量和维护性。AST模块可以帮助我们对Python代码进行重构,例如,将代码中的print语句替换为logging模块。

import ast

# 重构代码,将所有的print语句替换为调用logging模块
class MyTransformer(ast.NodeTransformer):
    def visit_Print(self, node):
        print_node = ast.Name(id="logging.info", ctx=ast.Load())
        args_node = [ast.Str(s="Print statement at line {0}".format(node.lineno))]
        args_node += node.values
        return ast.Expr(value=ast.Call(func=print_node, args=args_node, keywords=[]))

# 解析源代码,获得抽象语法树对象
tree = ast.parse("print('Hello, world!')\n")

# 将抽象语法树对象重构为新的语法树对象
my_transformer = MyTransformer()
new_tree = my_transformer.visit(tree)

# 使用astunparse将语法树源代码还原为字符串
import astunparse
print(astunparse.unparse(new_tree))

以上代码定义了一个重构器类MyTransformer,它继承自NodeTransformer并重写了visit_Print方法。visit_Print方法指在遍历print语句节点时被调用,可以在该方法中进行替换操作。


参考:

你知道什么是AST语法树嘛?你真的了解AST语法树嘛?读到最后你将对AST语法树有新的认识! - 知乎

ast --- 抽象语法树 — Python 3.12.1 文档

Python Ast抽象语法树的介绍及应用详解 - Python技术站

https://www.cnblogs.com/qiulinzhang/p/14258626.html

Python中ast模块是什么?_笔记大全_设计学院

【python】ast模块介绍和使用_import ast-CSDN博客

不要再手动批量替换了,使用python AST模块批量替换_Python_阿呆_InfoQ写作社区

https://www.cnblogs.com/yssjun/p/10069199.html

python语法树可视化_mob649e815b1a71的技术博客_51CTO博客

Python中ast模块是什么?_笔记大全_设计学院

https://www.cnblogs.com/qiulinzhang/p/14258626.html

python 可视化AST、CFG学习_python显示ast树形状-CSDN博客

【python】ast模块介绍和使用_import ast-CSDN博客

Using IPython for AST Visualization — ctree alpha documentation 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/320973.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

原子类-入门介绍和分类说明、基本类型原子类

Atomic翻译成中文是原子的意思。在化学上,我们知道原子是构成一般物质的最小单位,在化学 反应中是不可分割的。在我们这里Atomic是指一个操作是不可中断的。即使是在多个线程一起执 行的时候,一个操作一旦开始,就不会被其他线程干扰。 基本类型原子类 AtomicInteger:整…

QT上位机开发(权限管理)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 如果只是简单的工具软件,其实是没有权限管理这么一说的。比如说,串口工具、485工具之类的软件,其实根本不存在所…

哈希表与哈希算法(Python系列30)

在讲哈希表数据结构和哈希算法之前,我想先刨析一下数组和python中的列表 首先来讲一下数组,我想在这提出一个疑问: 为什么数组通过索引查询数据的时间复杂度为O(1),也就是不管数组有多大,算法的执行时间都是不变的。…

数据在AI图像修复任务中的核心作用

在人工智能(AI)领域,数据的重要性不言而喻。尤其在图像修复任务中,数据的精度和质量直接影响着AI模型的性能。图像修复是指利用AI技术自动识别图像中的缺陷或遮挡物,并对其进行修复或还原的过程。这项技术广泛应用于各…

LLM大模型和数据标注

对于那些不精通机器学习的人来说,像 ChatGPT 所基于的 GPT-3.5 这样的大型语言模型似乎是自给自足的。这些模型通过无监督或自我监督学习进行训练。简而言之,只需极少的人工干预,就能生成一个能像人类一样对话的模型。 这就引出了一个问题--…

3.三极管和MOS管

3.三极管和MOS管 基础知识三极管是电流控制型器件MOS管是电压控制型器件 分类及引脚定义电流导通方向基础应用常用MOS管电平转换电路MOS管实现电平转换用MOS管实现的“I2C总线电平转换电路”,实现3.3V电压域与5V电压域间的双向通讯 基础知识 三极管是电流控制型器件…

C语言中关于指针的理解及用法

关于指针意思的参考:https://baike.baidu.com/item/%e6%8c%87%e9%92%88/2878304 指针 指针变量 地址 野指针 野指针就是指针指向的位置是不可知的(随机的,不正确的,没有明确限制的) 以下是导致野指针的原因 1.指针…

ZZULIOJ 1110: 最近共同祖先(函数专题)

题目描述 如上图所示,由正整数1, 2, 3, ...组成了一棵无限大的二叉树。从某一个结点到根结 点(编号是1 的结点)都有一条唯一的路径,比如从10 到根结点的路径是(10, 5, 2, 1), 从4 到根结点的路径是(4, 2, 1)&#xff0…

【python playwright 安装及验证】

python playwright pip install playwright pip install playwright -i http://mirrors.aliyun.com/pypi/simple/ playwright codegen -o script.py -b chromium --ignore-https-errors --viewport-size “2560,1440” --proxy-server “http://100.8.64.8:60497” https://w…

xtu oj 1340 wave

题目描述 一个n列的网格,从(0,0)网格点出发,波形存在平波(从(x,y)到(x1,y)),上升波(从(x,y)到(x1,y1)),下降波(从(x,y)到(x1,y−1))三种波形,请问从(0,0)出发,最终到达(n,0)的不同波形有多少种&#xff1f…

x-cmd pkg | jless - 受 Vim 启发的命令行 JSON 查看器

目录 简介首次用户功能特点类似工具与竞品进一步探索 简介 jless 是一个命令行 JSON 查看器,设计用于读取、探索和搜索 JSON 数据。可以使用它来替代 less 、 jq 、 cat 以及您当前用于查看 JSON 文件的编辑器的任何组合。它是用 Rust 编写的,可以作为单…

LINUX基础培训六之磁盘和文件系统管理

前言、本章学习目标 掌握fdisk分区类型和管理分区了解parted分区类型掌握LVM模式文件系统创建、扩展、缩小文件系统 一、磁盘的分区管理 在 Linux 中有专门的分区命令 fdisk 和 parted。其中 fdisk 命令较为常用,但不支持大于 2TB 的分区;如果需要支…

QT上位机开发(usb设备访问)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 利用usb接口访问底层下位机,这是一种很常见的方式。目前比较简单的做法有两种,一种是usb转串口,另外一种是利用…

arcgis javascript api4.x加载天地图web墨卡托(wkid:3857)坐标系

效果&#xff1a; 示例代码&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><meta http-equiv&quo…

BRC20通证的诞生与未来展望!如何导入bitget教程

BRC-20通证是什么&#xff1f; 嘿&#xff01;你知道BRC-20通证吗&#xff1f;这可是比特币区块链上的超级明星&#xff01;它们不依赖智能合约&#xff0c;而是把JSON代码刻在聪上&#xff0c;聪可是比特币的最小单位哦&#xff01;就像在比特币的乐高积木上盖房子&#xff0…

JDK8终将走进历史,Oracle宣布JDK继续免费

目录 前言Oracle 已免费提供 JDKOracle Java SE 产品最新动态 为什么业界中用JDK8那么多Java SE 8 公共更新结束总结 前言 今天想到上个月无意中听闻到的一句话&#xff1a;JDK8之后收费了&#xff0c;所以大家都用JDK8。当时只觉得这个话说得不对&#xff0c;但因为和说话的人…

ubantu系统运维命令,端口相关操作

1、使用sudo ufw status命令查看所有开放的端口&#xff0c;如下图&#xff1a; 2、使用命令sudo ufw allow 8443&#xff0c;打开端口8443.如下图&#xff1a; 3、使用 sudo ufw reload刷新端口配置&#xff0c;如下图&#xff1a;

如何挖掘过期老域名并注册一个 DA 为 10 的高价值老域名

原文来源&#xff1a;https://guomuyu.com/registered-a-high-value-domain.html 最近有一些有意从事外贸的朋友阅读了《2024最新外贸建站&#xff1a;WordPress自建外贸独立站教程》这篇文章。然而&#xff0c;当他们尝试注册与自己所从事行业相关的域名时&#xff0c;却发现…

【电源专题】案例:不同模块同一个管脚默认状态不一样会导致什么异常?

案例背景:在产品设计中,有时候会兼容两个不同供应商同一个方案的模块。比如两个供应商使用的内部方案都是一样的芯片,封装也是兼容的。但是由于专利、LAYOUT方便、软件开发方便等角度来看,可能会存在不同模块供应商的同一个PIN脚对应的芯片内部的管脚不一样。管脚不一样那么…

训练FastestDet(Anchor-Free、参数量仅0.24M),稍改代码使得符合YOLO数据集排布

文章目录 0 参考链接1 准备数据1.1 使用以下代码生成绝对路径的txt文件1.2 在config文件夹下新建一个xxx.names文件 2 配置训练参数3 稍改代码使得符合YOLO数据集排布4 开始训练 0 参考链接 官方的代码&#xff1a;FastestDet 1 准备数据 我已有的数据集排布&#xff1a;&am…