关于Python:9. 深入理解Python运行机制

一、Python内存管理(引用计数、垃圾回收)

Python(CPython)采用的是:

“引用计数为主,垃圾回收为辅” 的内存管理机制。

也就是说:

  • 引用计数机制:负责大部分内存释放,简单、高效。

  • 垃圾回收机制(GC):处理循环引用等引用计数解决不了的问题。

1. 引用计数(Reference Counting)

每个 Python 对象都有一个“引用计数”,表示当前有多少个地方在使用它。

一旦引用计数为 0,Python 立刻销毁对象、释放内存。

举例说明:

a = [1, 2, 3]  # 创建列表对象,引用计数 = 1
b = a          # b 也指向这个列表,对象引用计数 = 2
del a          # 删除 a,引用计数 = 1
del b          # 删除 b,引用计数 = 0,对象被销毁

可以使用 sys.getrefcount() 查看一个对象的引用计数:

import sys
x = [1, 2, 3]
print(sys.getrefcount(x))  # 注意:结果比你预期的多1,因为参数x也算一次引用

引用计数的优点:

  • 实时释放内存:对象不用的时候,立刻回收。

  • 实现简单:不需要像 Java 那样复杂的 GC 跑一遍遍。

2. 引用计数的缺陷:循环引用

如果两个对象互相引用对方,即使外部没有引用了,引用计数也不会为0,内存就不会释放,导致 内存泄露

示例:

class Node:def __init__(self):self.ref = Nonea = Node()
b = Node()
a.ref = b
b.ref = adel a
del b# a和b虽然没有外部引用,但它们互相引用,所以内存不会释放!

在这个例子中:

对象谁引用了它?引用计数
amain作用域、b.ref2
bmain作用域、a.ref2

现在执行 del adel b

对象谁引用了它?引用计数
a只剩 b.ref1
b只剩 a.ref1

引用计数永远不会变为 0!

3. 垃圾回收(Garbage Collection,GC)

为了解决循环引用的问题,Python 内部设计了一个 GC模块 来“定期清理”不再使用的内存。

这个 GC 是通过 “分代回收(Generational GC)” 实现的。

分代回收模型(Generational GC)

Python 把所有的对象分成 3 代:

代数含义特点
第0代新建对象最频繁回收
第1代经常存活下来的对象偶尔回收
第2代长期存在的对象很少回收,数量最多

GC 机制假设:

“活得越久的对象,越有可能继续活着”,所以越老的代回收越少。

回收的流程

  • Python 会监控“分配了多少个对象”和“删除了多少个对象”。

  • 当分配/删除数量超过阈值,就触发 GC:

    • 先回收第0代

    • 如果仍然触发阈值,就继续回收第1代、第2代

  • 采用标记-清除算法:标记“可达对象”,删除“不可达对象”

GC 模块使用示例:

import gc# 查看当前是否启用了自动GC
print(gc.isenabled())  # True# 手动触发一次GC
gc.collect()# 查看GC统计信息
print(gc.get_count())  # 返回 (第0代对象数量, 第1代, 第2代)

4. 小对象内存池机制

CPython 还对**小对象(小于 512 字节)**做了优化:

  • 使用内存池(称为 pymalloc)重复利用内存块。

  • 这样避免频繁向操作系统申请/释放内存,提高性能。

这就是为什么写 a = 10; b = 10 时,两个变量可能指向的是同一个对象地址

5. 总结

机制原理优点缺点
引用计数每个对象维护引用计数,0即销毁实时、高效不能处理循环引用
垃圾回收分代收集,标记清除弥补引用计数的缺陷增加一些系统开销
小对象池内存池优化小对象提高小对象复用效率占用一些额外内存

二、Python解释器(CPython、PyPy)

Python 解释器就是把你写的 .py 源码翻译为计算机能执行的“指令”的程序。

目前主流解释器有多个实现:

名称语言实现特点适用场景
CPython用 C 写的官方标准实现,最常用默认解释器,稳定
PyPy用 RPython 写支持 JIT,速度更快追求性能
Jython用 Java 写可运行在 JVM 上,调用 JavaJava 环境
IronPython.NET 实现支持 C#/VB 调用.NET 环境
MicroPythonC实现(精简)运行在嵌入式设备单片机开发

1. CPython(最主流、最重要)

  • CPython 是 Python 的官方实现

  • 是用 C语言 写的解释器(Interpreter)。

  • 所有你运行 .py 的地方,默认就是 CPython。

$ python3 --version
Python 3.11.7  ← 这就是 CPython

CPython 的执行流程

Python 源码(.py 文件)↓
词法/语法分析↓
AST(抽象语法树)↓
编译成字节码(.pyc 文件)↓
交由 CPython 的虚拟机解释执行

这也是为什么会看到 .pyc 文件,它其实就是:

        Python 的“中间语言”,类似 Java 的 .class 文件。

CPython 底层结构

typedef struct _object {Py_ssize_t ob_refcnt;     // 引用计数PyTypeObject *ob_type;    // 类型信息
} PyObject;

CPython(Python 的官方解释器)就是用 C 语言编写的

Py_ssize_t ob_refcnt;       // 当前对象的引用计数,决定是否自动释放
PyTypeObject *ob_type;      // 指向该对象的类型结构体,比如 int、str 类型

每个 Python 中的对象(int、list、dict 等)在底层都是一个 PyObject 结构体,通过这个结构可以知道:

  • 它被引用了多少次(用于自动内存管理)

  • 它到底是什么类型(用于运行时类型识别)

CPython 的缺陷:GIL(全局解释器锁)

CPython 使用 GIL 来确保多线程安全:

Global Interpreter Lock→ 同一时间,只允许一个线程执行 Python 字节码

这意味着 Python 的多线程并不能真正并发运行计算密集型任务。CPU 利用率低。

2. PyPy(性能极致的解释器)

  • PyPy 是用一种叫 RPython(可静态类型的Python子集) 实现的 Python 解释器。

  • 最大亮点是 JIT(Just-In-Time)编译技术,可以把热代码编译成机器码,加速运行。

PyPy 的优势

特性说明
 JIT 编译PyPy 会将频繁执行的代码“编译为机器码”加速运行
 智能优化内联、移除多余变量等操作自动完成
⏱ 性能提升实测一般比 CPython 快 4~10 倍

例如:

from time import timedef f():s = 0for i in range(10_000_000):s += ireturn sstart = time()
f()
print("耗时:", time() - start)

同样的代码,用 PyPy 跑会明显快很多。

如何下载安装 PyPy:

1)访问官网下载页面:https://www.pypy.org/download.html

2)解压缩到一个目录,比如:C:\pypy3.9

3)打开命令行,输入:

C:\pypy3.9\pypy3.exe

如果是 Linux / Mac 用户:

sudo apt install pypy3       # Ubuntu
# 或
brew install pypy3           # Mac(需安装 Homebrew)

4)运行代码用 PyPy 解释器

比如:

pypy3 your_script.py

或者:

pypy3
>>> print("hello from PyPy!")

这样就不再用默认的 CPython 。


三、编译原理初步(AST抽象语法树、字节码)

Python 作为一种 解释型语言,它的代码执行过程包括了“编译”和“解释”两个阶段:

编译阶段:把 Python 源代码编译成一种中间表示形式(字节码),这个字节码并不是直接机器码,而是 Python 虚拟机能理解的低级指令。

解释执行阶段:字节码由 Python 解释器(虚拟机)执行。

这两个过程中的重要组成部分就是 AST(抽象语法树) 和 字节码。

1. 什么是 AST(抽象语法树)?

AST(抽象语法树) 是对 Python 源代码的一种树状表示,它的每个节点代表了 Python 程序中的一个结构元素(如表达式、语句等)。它比语法树更简洁,去除了与编程语言具体语法无关的部分。

  • AST 是 Python 编译阶段的产物。

  • Python 代码首先通过 词法分析(将源码转换为标记)和 语法分析(将标记转换为 AST)两步生成。

  • 在 AST 之上,Python 可以进行代码优化、分析、转换、生成字节码等操作。

 AST 的重要性

  • 代码分析与优化:通过 AST,你可以分析代码的结构,做静态分析(例如变量的使用、控制流等)或进行代码优化。

  • 代码转换:AST 是生成字节码的基础,修改 AST 可以进行代码重写、反混淆等操作。

  • 反向工程:通过分析 AST,你可以将混淆代码或压缩代码还原为易懂的原始代码结构。

生成 AST

可以使用 Python 的 ast 模块来分析和操作 Python 的 AST。例如,下面的代码将 Python 代码解析成 AST:

import astsource_code = "x = 1 + 2 * 3"
tree = ast.parse(source_code)# 打印 AST 的结构
ast.dump(tree, indent=4)

输出会是类似这样的结构(树状结构):

Module(body=[Assign(targets=[Name(id='x', ctx=Store())],value=BinOp(left=Num(n=1), op=Add(), right=BinOp(left=Num(n=2), op=Mult(), right=Num(n=3))))]
)

这就是 Python 代码的 AST 结构。可以看到,它把算式 1 + 2 * 3 转换成了一个树状结构,节点包含了操作符、操作数、表达式等。

AST 操作示例

通过操作 AST,你可以修改代码结构,比如实现代码重构、自动化修改等:

class MyTransformer(ast.NodeTransformer):def visit_BinOp(self, node):if isinstance(node.op, ast.Add):node.op = ast.Sub()  # 把加法变成减法return node# 修改 AST
transformer = MyTransformer()
transformed_tree = transformer.visit(tree)# 查看修改后的树
ast.dump(transformed_tree, indent=4)

2. 什么是字节码(Bytecode)?

字节码 是一种中间代码,Python 将源代码编译成字节码后,由 Python 的虚拟机(PVM)解释执行。它比源代码更接近机器码,但仍然独立于平台。

  • 字节码的作用:通过将源码编译成字节码,Python 可以实现跨平台运行,只需要安装 Python 解释器,就能执行相同的字节码。

  • .pyc 文件:字节码通常保存在 .pyc 文件中,位于 __pycache__ 目录下。Python 会根据文件的修改时间来决定是否重新编译。

字节码的生成过程

  1. 编译:Python 源代码通过 CPython 编译器转换为字节码(.pyc 文件)。每次你运行 .py 文件时,CPython 会首先检查是否有 .pyc 文件,如果没有就编译成字节码。

  2. 执行:字节码通过 Python 虚拟机(PVM)解释执行。

字节码反汇编

使用 dis 模块,可以看到 Python 代码的字节码。例如:

import disdef example():a = 1 + 2return adis.dis(example)

输出结果会显示字节码:

  2           0 LOAD_CONST               1 (1)2 LOAD_CONST               2 (2)4 BINARY_ADD6 STORE_NAME               0 (a)8 LOAD_NAME                0 (a)10 RETURN_VALUE

这说明:

  • 先把常量 12 加载到栈上(LOAD_CONST),

  • 执行加法(BINARY_ADD),

  • 把结果存到变量 a 中(STORE_NAME)。

字节码与机器码的关系

字节码比源代码更接近机器码,但它仍然不是直接可以由 CPU 执行的代码。在 CPython 中,字节码是由 Python 解释器逐条解释执行的。而机器码则是直接由硬件执行的代码。

3. 结合 AST 和 字节码的实际应用

应用场景:代码重构、反混淆

在做 JS 逆向APP 逆向时,很多时候需要分析混淆代码并重构它。在 Python 中,AST 是一个非常好的工具,可以帮助理解代码的结构,进行重构、逆向等操作。

例子:重构混淆代码

比如,将一段复杂的运算式改写为更易读的形式:

# 混淆的代码
x = 1 + 2 * 3# 解析并重构为
x = 7

通过 AST,可以提取出原始的运算结构,并将它们转换为更容易理解的形式。

应用场景:性能优化

在性能优化中,了解 Python 如何将源代码转化为字节码,有助于优化代码,避免不必要的计算、内存分配等操作。例如,避免频繁的内存分配、减少不必要的对象创建,可以提高程序的性能。

术语含义作用
AST抽象语法树,一种树状结构代码结构分析、重构、静态分析、代码生成和优化
字节码中间代码,比源码更低级,但不依赖平台Python 跨平台运行,虚拟机解释执行

四、C扩展开发(如需要极限优化)

C 扩展就是使用 C 语言编写的 Python 模块,它可以被 Python 调用,像普通模块一样 import

为什么要用 C 写 Python 模块?

  • Python 本身是用 C 写的(CPython)

  • Python 解释型的性能较低,尤其在数值计算、循环密集任务中较慢

  • C 语言性能高,可以编写性能关键模块,再用 Python 调用

  • 还能直接调用操作系统或硬件资源,或者加载第三方底层库(如 OpenSSL)

实际例子:写一个简单的 C 扩展模块

创建一个 C 模块 myfast.c,提供一个加法函数给 Python 调用。

文件结构:

myfast/
├── myfast.c
└── setup.py

1) myfast.c 内容如下:

#include <Python.h>// C函数:两个数相加
static PyObject* add(PyObject* self, PyObject* args) {int a, b;// 从 Python 传入参数解析为 C 中的 intif (!PyArg_ParseTuple(args, "ii", &a, &b)) {return NULL;}// 返回一个 Python 整数return PyLong_FromLong(a + b);
}// 定义方法表
static PyMethodDef MyFastMethods[] = {{"add", add, METH_VARARGS, "Add two numbers"},{NULL, NULL, 0, NULL}
};// 定义模块
static struct PyModuleDef myfastmodule = {PyModuleDef_HEAD_INIT,"myfast",       // 模块名NULL,           // 文档(可为 NULL)-1,MyFastMethods
};// 初始化模块
PyMODINIT_FUNC PyInit_myfast(void) {return PyModule_Create(&myfastmodule);
}

2) setup.py 构建脚本:

from setuptools import setup, Extensionmodule = Extension('myfast', sources=['myfast.c'])setup(name='myfast',version='1.0',description='A demo C extension for Python',ext_modules=[module]
)

3)编译模块

运行:

python setup.py build_ext --inplace

这会生成一个 .so 文件(Linux/mac)或 .pyd(Windows),然后就可以像普通模块一样导入:

4)Python 中使用

import myfastprint(myfast.add(3, 5))  # 输出 8

高级应用方向

调用已有 C/C++ 库(如 OpenSSL、libcurl)

可以封装 C 函数为 Python 接口,甚至是使用 extern 引用已有 .so/.dll

写“黑盒”模块

很多商业代码、加密算法,甚至反爬参数逻辑会被写成 C 模块,然后 只暴露一个接口给 Python,大大提高逆向难度。

 用于性能关键场景

在图像处理、数据加密、音视频处理、矩阵运算、机器学习中,Python 常调用 NumPy(内部也是 C 实现)。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【STM32单片机】#13 RTC实时时钟

主要参考学习资料&#xff1a; B站江协科技 STM32入门教程-2023版 细致讲解 中文字幕 开发资料下载链接&#xff1a;https://pan.baidu.com/s/1h_UjuQKDX9IpP-U1Effbsw?pwddspb 单片机套装&#xff1a;STM32F103C8T6开发板单片机C6T6核心板 实验板最小系统板套件科协 目录 Uni…

SecureCRT 使用指南:安装、设置与高效操作

目录 一、SecureCRT 简介 1.1 什么是 SecureCRT&#xff1f; 1.2 核心功能亮点 1.3 软件特点 二、SecureCRT 安装与激活 2.1 安装步骤&#xff08;Windows 系统&#xff09; 2.2 激活与破解&#xff08;仅供学习参考&#xff09; 三、基础配置与优化 3.1 界面与编码设…

3.5/Q1,GBD数据库最新一区文章解读

文章题目&#xff1a;Global burden of low vision and blindness due to age-related macular degeneration from 1990 to 2021 and projections for 2050 DOI&#xff1a;10.1186/s12889-024-21047-x 中文标题&#xff1a;1990年至2021年因年龄相关性黄斑变性导致的低视力和失…

【Hive入门】Hive安全管理与权限控制:基于SQL标准的授权GRANT REVOKE深度解析

目录 引言 1 Hive权限模型概述 2 SQL标准授权基础 2.1 核心概念解析 2.2 授权模型工作流程 3 GRANT/REVOKE语法详解 3.1 基础授权语法 3.2 权限回收语法 3.3 参数说明 4 授权场景 4.1 基础授权示例 4.2 列级权限控制 4.3 视图权限管理 5 权限查询与验证 5.1 查看…

无缝监控:利用 AWS X-Ray 增强 S3 跨账户复制的可见性

您准备好提升您的云和 DevOps 技能了吗? 🐥《云原生devops》专门为您打造,我们精心打造的 30 篇文章库,这些文章涵盖了 Azure、AWS 和 DevOps 方法论的众多重要主题。无论您是希望精进专业知识的资深专业人士,还是渴望学习相关知识的新手,这套资源库都能满足您的需求。 …

Python Cookbook-7.2 使用 pickle 和 cPickle 模块序列化数据

任务 你想以某种可以接受的速度序列化和重建Python 数据结构&#xff0c;这些数据既包括基本Python 对象也包括类和实例。 解决方案 如果你不想假设你的数据完全由基本 Python 对象组成&#xff0c;或者需要在不同的 Python 版本之间移植&#xff0c;再或者需要将序列化后的…

2025.5.5总结

今日感悟&#xff1a;这假期就这样结束了&#xff0c;玩了一次滑板&#xff0c;打扫了一次租房&#xff0c;出去逛了一次街&#xff0c;看完了一本书&#xff0c;追了一部剧。既没有家人&#xff0c;也没有能一同畅饮的同学&#xff0c;更没有对象&#xff0c;显得确实有些孤独…

MySQL | DQL语句-连接查询

MySQL | DQL语句-连接查询 &#x1fa84;个人博客&#xff1a;https://vite.xingji.fun 什么是连接查询 从一张表中查询数据称为单表查询。从两张或更多张表中联合查询数据称为多表查询&#xff0c;又叫做连接查询。什么时候需要使用连接查询&#xff1f; 比如这样的需求&…

Vite简单介绍

Vite 是一个现代化的前端构建工具&#xff0c;由 Vue.js 的创始人 Evan You 开发&#xff0c;旨在提供更快的开发体验和更高效的构建流程。它的名字来源于法语单词“vite”&#xff0c;意为“快速”&#xff0c;这也反映了它的核心优势——极速的冷启动和热模块替换&#xff08…

C语言-回调函数

回调函数 通过函数指针调用函数&#xff0c;而这个被调用的函数称为回调函数 回调函数是C语言中一种强大的机制&#xff0c;允许将函数作为参数传递给其他函数&#xff0c;从而在特定时机由后者调用。它的核心在于函数指针的使用 以下是回调函数的使用例子 先创建好一个函数…

启发式算法-禁忌搜索算法

禁忌搜索是一种可以用于解决组合优化问题的启发式算法&#xff0c;通过引入记忆机制跳出局部最优&#xff0c;避免重复搜索。该算法从一个初始解开始&#xff0c;通过邻域搜索策略来寻找当前解的邻域解&#xff0c;并在邻域解中选择一个最优解作为下一次迭代的当前解&#xff0…

Python 整理3种查看神经网络结构的方法

1. 网络结构代码 import torch import torch.nn as nn# 定义Actor-Critic模型 class ActorCritic(nn.Module):def __init__(self, state_dim, action_dim):super(ActorCritic, self).__init__()self.actor nn.Sequential(# 全连接层&#xff0c;输入维度为 state_dim&#xf…

Linux 查询CPU飙高的原因

获取进程ID ps -efgrep xxxx查询占用最高的线程ID top -Hp 线程ID线程ID 转 16进制数 printf 0x%x\n 线程ID基于jstack工具 跟踪堆栈定位代码位置 jstack 进程ID | grep 16禁止线程ID -A 20

Oracle OCP认证考试考点详解083系列09

题记&#xff1a; 本系列主要讲解Oracle OCP认证考试考点&#xff08;题目&#xff09;&#xff0c;适用于19C/21C,跟着学OCP考试必过。 41. 第41题&#xff1a; 题目 解析及答案&#xff1a; 关于应用程序容器&#xff0c;以下哪三项是正确的&#xff1f; A) 它可以包含单个…

GESP2024年3月认证C++八级( 第二部分判断题(1-5))

孙子定理参考程序&#xff1a; #include <iostream> #include <vector> using namespace std;// 扩展欧几里得算法&#xff1a;用于求逆元 int extendedGCD(int a, int b, int &x, int &y) {if (b 0) {x 1; y 0;return a;}int x1, y1;int gcd extende…

C 语言比较运算符:程序如何做出“判断”?

各类资料学习下载合集 ​​https://pan.quark.cn/s/8c91ccb5a474​​ 在编写程序时,我们经常需要根据不同的条件来执行不同的代码。比如,如果一个分数大于 60 分,就判断为及格;如果用户的年龄小于 18 岁,就禁止访问某个内容等等。这些“判断”的核心,就依赖于程序能够比…

WITH在MYSQL中的用法

WITH 子句&#xff08;也称为公共表表达式&#xff0c;Common Table Expression&#xff0c;简称 CTE&#xff09;是 SQL 中一种强大的查询构建工具&#xff0c;它可以显著提高复杂查询的可读性和可维护性。 一、基本语法结构 WITH cte_name AS (SELECT ... -- 定义CTE的查询…

多序列比对软件MAFFT介绍

MAFFT(Multiple Alignment using Fast Fourier Transform)是一款广泛使用且高效的多序列比对软件,由日本京都大学的Katoh Kazutaka等人开发,最早发布于2002年,并持续迭代优化至今。 它支持从几十条到上万条核酸或蛋白质序列的快速比对,同时在准确率和计算效率之间提供灵…

APP 设计中的色彩心理学:如何用色彩提升用户体验

在数字化时代&#xff0c;APP 已成为人们日常生活中不可或缺的一部分。用户在打开一个 APP 的瞬间&#xff0c;首先映入眼帘的便是其色彩搭配&#xff0c;而这些色彩并非只是视觉上的装饰&#xff0c;它们蕴含着强大的心理暗示力量&#xff0c;能够潜移默化地影响用户的情绪、行…

Compose 中使用 WebView

在 Jetpack Compose 中&#xff0c;我们可以使用 AndroidView 组件来集成传统的 Android WebView。以下是几种实现方式&#xff1a; 基础 WebView 实现 Composable fun WebViewScreen(url: String) {AndroidView(factory { context ->WebView(context).apply {// 设置布局…