python列表(list)底层实现

news2024/9/21 13:17:50

list

Python内存管理中的基石

Python中所有类型创建对象时,底层都是与PyObject和PyVarObject结构体实现,一般情况下由单个元素组成对象内部会使用PyObject结构体(float)、由多个元素组成的对象内部会使用PyVarObject结构体

2个结构体

  1. PyObject,此结构体中包含3个元素。
    1. _PyObject_HEAD_EXTRA,用于构造双向链表。
    2. ob_refcnt,引用计数器。
    3. *ob_type,数据类型。
  2. PyVarObject,次结构体中包含4个元素(ob_base中包含3个元素)
    1. ob_base,PyObject结构体对象,即:包含PyObject结构体中的三个元素。
    2. ob_size,内部元素个数。

3个宏定义

  1. PyObject_HEAD,代指PyObject结构体。
  2. PyVarObject_HEAD,代指PyVarObject对象。
  3. _PyObject_HEAD_EXTRA,代指前后指针,用于构造双向队列。

空list占多大内存

print(list().__sizeof__()) # 40

list结构体

// PyVarObject
typedef struct {
	// PyObject 
    PyObject ob_base;
    
    // 列表长度,int类型,8字节
    Py_ssize_t ob_size;
} PyVarObject;

#define PyObject_VAR_HEAD      PyVarObject ob_base;

typedef _W64 int Py_ssize_t;

typedef struct _object {
    _PyObject_HEAD_EXTRA

    // 引用计数器int类型,8字节
    Py_ssize_t ob_refcnt; 
    // 指针,8字节
    PyTypeObject *ob_type;
} PyObject;

typedef struct {
    PyObject_VAR_HEAD
    
    // 指向列表元素的指针向量。 列表[0] 是ob_item[0],等等。
    // 指针的指针,存放列表元素
    // 为什么要用指针的指针呢?
    // python的list并不直接保存元素,而是保存元素的指针,这也是同一个list中可以同时存放多种类型元素的根本原因。
    // 指针8字节
    PyObject **ob_item; 
    
	// 列表容量,int类型,8字节
    Py_ssize_t allocated;
} PyListObject;

创建列表

PyObject *
PyList_New(Py_ssize_t size)
{	
	// 处理size < 0的情况
    if (size < 0) {
    	// 打印错误日志
        PyErr_BadInternalCall();
        return NULL;
    }
	
    struct _Py_list_state *state = get_list_state();
    PyListObject *op;
#ifdef Py_DEBUG
    // PyList_New() must not be called after _PyList_Fini()
    assert(state->numfree != -1);
#endif
    if (state->numfree) {
        state->numfree--;
        op = state->free_list[state->numfree];
        _Py_NewReference((PyObject *)op);
    }
    else {
        op = PyObject_GC_New(PyListObject, &PyList_Type);
        if (op == NULL) {
            return NULL;
        }
    }
	
	
    if (size <= 0) {
        op->ob_item = NULL;
    }
    else {
    	// 给item分配内存
        op->ob_item = (PyObject **) PyMem_Calloc(size, sizeof(PyObject *));
        if (op->ob_item == NULL) {
            Py_DECREF(op);
            return PyErr_NoMemory();
        }
    }
    
    // 设置len=0
    Py_SET_SIZE(op, size);
    // 设置cap=0
    op->allocated = size;
    _PyObject_GC_TRACK(op);
	
	// 返回列表指针
    return (PyObject *) op;
}

添加元素

listType = list()
ele = "德玛西亚"
// 实际上是将内存地址放到了列表中,即存储了一个指针,固定大小为8字节
listType.append(ele)

在这里插入图片描述

static int
app1(PyListObject *self, PyObject *v)
{	
	// 获取列表len
    Py_ssize_t n = PyList_GET_SIZE(self);

    assert (v != NULL);
    assert((size_t)n + 1 < PY_SSIZE_T_MAX);
	
	/* list_resize
	Add padding to make the allocated size multiple of 4.
    The growth pattern is:  0, 4, 8, 16, 24, 32, 40, 52, 64, 76, ... 
    */
    // 判断cap是否够,n+1因为操作是增加元素.
    if (list_resize(self, n+1) < 0)
        return -1;
	
	// 追加元素
    Py_INCREF(v);
    PyList_SET_ITEM(self, n, v);
    return 0;
}

int
PyList_Append(PyObject *op, PyObject *newitem)
{
    if (PyList_Check(op) && (newitem != NULL))
        return app1((PyListObject *)op, newitem);
    PyErr_BadInternalCall();
    return -1;
}

插入元素

在这里插入图片描述

static int
ins1(PyListObject *self, Py_ssize_t where, PyObject *v)
{
	// 计算len
    Py_ssize_t i, n = Py_SIZE(self);
    PyObject **items;
    if (v == NULL) {
        PyErr_BadInternalCall();
        return -1;
    }

    assert((size_t)n + 1 < PY_SSIZE_T_MAX);
    // 扩容或者缩容
    if (list_resize(self, n+1) < 0)
        return -1;

    if (where < 0) {
        where += n;
        if (where < 0)
            where = 0;
    }
    if (where > n)
        where = n;
        
	// 移位
    items = self->ob_item;
    for (i = n; --i >= where; )
        items[i+1] = items[i];
        
    Py_INCREF(v);
    items[where] = v;
    return 0;
}

int
PyList_Insert(PyObject *op, Py_ssize_t where, PyObject *newitem)
{
    if (!PyList_Check(op)) {
        PyErr_BadInternalCall();
        return -1;
    }
    return ins1((PyListObject *)op, where, newitem);
}

删除元素

只将len减少1,如果下次append操作,会将第三个覆盖。
在这里插入图片描述

static PyObject *
list_pop_impl(PyListObject *self, Py_ssize_t index)
/*[clinic end generated code: output=6bd69dcb3f17eca8 input=b83675976f329e6f]*/
{
    PyObject *v;
    int status;
	
	// if len(list) == 0 触发panic
    if (Py_SIZE(self) == 0) {
        /* Special-case most common failure cause */
        PyErr_SetString(PyExc_IndexError, "pop from empty list");
        return NULL;
    }
	// 如果索引小于0,index=len(list)+index
    if (index < 0)
        index += Py_SIZE(self);
    // 索引越界
    if (!valid_index(index, Py_SIZE(self))) {
        PyErr_SetString(PyExc_IndexError, "pop index out of range");
        return NULL;
    }
	// 找到val
    v = self->ob_item[index];
    if (index == Py_SIZE(self) - 1) {
    	// 计算容量扩缩容
        status = list_resize(self, Py_SIZE(self) - 1);
        // 直接返回
        if (status >= 0)
            return v; /* and v now owns the reference the list had */
        else
            return NULL;
    }
    Py_INCREF(v);
    // 移动数据
    status = list_ass_slice(self, index, index+1, (PyObject *)NULL);
    if (status < 0) {
        Py_DECREF(v);
        return NULL;
    }
    return v;
}

清空元素

直接将结构体元素的指针置为 NULL即可,GC将没有引用的内存地址回收。

static int
_list_clear(PyListObject *a)
{
    Py_ssize_t i;
    PyObject **item = a->ob_item;
    if (item != NULL) {
        /* Because XDECREF can recursively invoke operations on
           this list, we make it empty first. */
        i = Py_SIZE(a);
        Py_SET_SIZE(a, 0);
	
		// 直接将item改为NULL
        a->ob_item = NULL;
        a->allocated = 0;
        while (--i >= 0) {
            Py_XDECREF(item[i]);
        }
		// 释放无用内存
        PyMem_Free(item);
    }
    /* Never fails; the return value can be ignored.
       Note that there is no guarantee that the list is actually empty
       at this point, because XDECREF may have populated it again! */
    return 0;
}

销毁列表

  1. 引用计数器>0,不操作
  2. 否则:将每一个元素的引用计数减一,再将结构体参数改为对应的空值,将PyListObject放入freelist(容量为80)中等待销毁。

创建列表会先去freelist中找一个拿出来,就不用再次申请内存销毁内存了。

在这里插入图片描述

扩缩容

static int
// Py_ssize_t : len长度,要新增就传入len+1,要删除就传入len-1
// PyListObject : list指针
list_resize(PyListObject *self, Py_ssize_t newsize)
{
    PyObject **items;
    size_t new_allocated, num_allocated_bytes;
	
	// allocated是cap
    Py_ssize_t allocated = self->allocated;

    // 如果cap大于len,并且 len >= (cap/2),表示cap足够,直接修改len,然后进行操作即可,返回状态码status=0
    if (allocated >= newsize && newsize >= (allocated >> 1)) {
        assert(self->ob_item != NULL || newsize == 0);
        // 设置结构体的len为newsize
        Py_SET_SIZE(self, newsize);
        return 0;
    }

    /* This over-allocates proportional to the list size, making room
     * for additional growth.  The over-allocation is mild, but is
     * enough to give linear-time amortized behavior over a long
     * sequence of appends() in the presence of a poorly-performing
     * system realloc().
     * Add padding to make the allocated size multiple of 4.
     * The growth pattern is:  0, 4, 8, 16, 24, 32, 40, 52, 64, 76, ...
     * Note: new_allocated won't overflow because the largest possible value
     *       is PY_SSIZE_T_MAX * (9 / 8) + 6 which always fits in a size_t.
     */

	// 扩容: cap < newsize
	// 缩容: newsize < (cap/2)
	// 扩缩容机制算法:((size_t)newsize + (newsize >> 3) + 6) & ~(size_t)3;
    new_allocated = ((size_t)newsize + (newsize >> 3) + 6) & ~(size_t)3;
    if (newsize - Py_SIZE(self) > (Py_ssize_t)(new_allocated - newsize))
        new_allocated = ((size_t)newsize + 3) & ~(size_t)3;

    if (newsize == 0)
        new_allocated = 0;
    if (new_allocated <= (size_t)PY_SSIZE_T_MAX / sizeof(PyObject *)) {
        num_allocated_bytes = new_allocated * sizeof(PyObject *);
        // 基于realloc进行扩缩容,可能涉及原来元素的迁移。
        items = (PyObject **)PyMem_Realloc(self->ob_item, num_allocated_bytes);
    }
    else {
        // integer overflow
        items = NULL;
    }
    if (items == NULL) {
        PyErr_NoMemory();
        return -1;
    }
	// 重新设置list结构体
    self->ob_item = items;
    Py_SET_SIZE(self, newsize);
    self->allocated = new_allocated;
    return 0;
}

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

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

相关文章

应用层——电子邮件(SMTP、POP3、IMAP)

目录 1. 电子邮件系统及组成结构 1.1 电子邮件 1.2 电子邮件系统的组件 2. SMTP(邮件发送协议) 2.1 SMTP的特征 2.2 SMTP的基本操作 2.3 SMTP协议的基本流程 2.4 SMTP交互与应答 2.5 SMTP与HTTP的对比 3. 邮件报文格式与MIME 3.1 电子邮件格式 3.2 多用途网际…

STM32时钟系统原理和作用

时钟对于单片机来讲类似于心脏对于人体&#xff0c;单片机通过时钟信号控制单片机工作。 1.时钟系统工作流程 时钟源输入----被分频或倍频----输出至系统/外设/外部接口 2.时钟分类 RC时钟精度低&#xff0c;体积小&#xff0c; OSC时钟精度高&#xff0c;体积大。 3.部分…

5.多层感知机

5.多层感知机 目录 感知机 基本内容训练感知机感知机存在的问题总结 多层感知机 隐藏层 单隐藏层-单分类 激活函数 ReLU函数sigmoid函数tanh函数总结 多类分类 多层感知机的从零开始实现 初始化模型参数激活函数模型损失函数训练 多层感知机的简洁实现 模型模型选择、欠拟合和…

⭐基于unity物体移动的方法API的总结-GIF图文详解⭐

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 秩沅 原创 收录于专栏 unity常用API ⭐相关文章&#xff1a; 线性差值函数以及平滑阻尼的运用和实践&#xff08;Lerp AND SmoothDa…

文献阅读:Towards Out-of-Distribution Sequential Event Prediction:A Causal Treatment

文献阅读&#xff1a;Towards Out-of-Distribution Sequential Event Prediction&#xff1a;A Causal Treatment 1. 问题描述2. 方法 & 模型设计 1. loss定义2. 模型设计 3. 实验 & 结果 1. 基础实验2. CaseQ策略细节分析 4. 结论 & 总结 文献链接&#xff1a;ht…

GDB调试学习

文章目录简介准备工作常用命令简介 ​GDB 全称“GNU symbolic debugger”&#xff0c;它诞生于 GNU 计划&#xff08;同时诞生的还有 GCC、Emacs 等&#xff09;&#xff0c;是 Linux 下常用的程序调试器。发展至今&#xff0c;GDB 已经迭代了诸多个版本&#xff0c;当下的 GD…

五个了解自己天赋优势的分析工具(二)盖洛普优势测评

盖洛普优势测评 盖洛普优势识别器&#xff08;也叫克利夫顿优势评估&#xff09;&#xff0c;是盖洛普公司旗下⼀款王牌产品&#xff0c;是⼀个专⻔⽤来识别⼀个⼈隐形天赋、潜在优势、驱动⼒来源的⼯具&#xff0c;它可以帮助⼀个⼈科学认识⾃⼰。 它把⼈的思维⽅式、感受形…

网络初识

一些基本概念认识IP地址概念&#xff1a;IP地址用于定位主机的网络地址&#xff0c;标识了网络上设备所在的位置。也就是说主机在哪。格式&#xff1a;IP地址是一个32位的二进制数&#xff0c;通常被分割为4个字节。通常我们使用“点分十进制”的方式来表示主机的IP地址——>…

2.2 SQL入门和实战

文章目录数据库介绍MySQL的入门应用SQL的基础与DDLSQL的分类&#xff1a;SQL的基础语法特性&#xff1a;DDL库管理&#xff1a;DDL表管理&#xff1a;SQL-DMLSQL-DQLDQL基础查询DQL分组聚合排序分页Python&MySQLcommit数据库介绍 数据库的层级&#xff1a;按照库->表-&…

WAL Write AheadLog

WAL即 Write Ahead Log&#xff0c;WAL的主要意思是说在将元数据的变更操作写入磁盘之前&#xff0c;先预先写入到一个log文件中 磁盘读取分为顺序读取和随机读取 内存读取也有顺序读取和随机读取 下图是磁盘&#xff0c;内存 顺序和随机读取的效率表 一般来说内存读取速度要…

硬件设计基础 —— 电流、电压、功率

目录 电流的形成 原子的结构 原子核与电子 电子的移动 电流的测量 电子的运动形成电流 电压的作用 功率 电流的形成 原子的结构 上图为原子结构示意图中间带正电的称为原子核带负电荷的电子围绕在他周围旋转原子核与电子 电子离原子核越远&#xff0c;外围电子越少&…

Cadence PCB仿真使用Allegro PCB SI进行数字电路毛刺容差设置的方法图文教程

⏪《上一篇》   🏡《总目录》   ⏩《下一篇》 目录 1, 概述2,配置方法3,总结1, 概述 数字信号的波形往往不是完美的低和高电平,经常会有一些毛刺。这在信号完整性仿真中也经常遇到,为了保证仿真顺利进行需要对接收电路的毛刺容差进行设置。本文简单介绍其配置方法。…

COCO_02 二值分割数据集制作为COCO数据集

文章目录1 前言2 分割数据集格式2.1 原始图像2.2 分割标注2.3 文件名信息3 制作COCO格式基本流程4 根据分割标注制作COCO标注&#xff08;核心&#xff09;Main1 前言 最近要开始实例分割任务&#xff0c;由于实例分割是建立再目标检测基础之上的&#xff0c;因此需要制作能用…

两道一年级数学题把我干废了

目录一、背景二、题目三、过程第一解&#xff1a;第二解&#xff1a;第三解&#xff1a;四、总结五、升华一、背景 最近在看小学一年级数学课本的时候&#xff0c;其中两道题让我感叹现在小学教育题的伟大和深厚&#xff0c;并且从中领悟到了…哈哈哈&#xff0c;先卖个官司&a…

【Ansible】Ansible 角色

Ansible 角色 文章目录Ansible 角色一、Ansible Roles 介绍二、Roles 结构三、role 的制作过程四、如何在PlayBook中使用 Role1. 经典&#xff08;原始&#xff09;方式2. 新方式五、如何使用 Galaxy一、Ansible Roles 介绍 一个数据中心有可能存在好多类型的服务器。比如&…

【博客588】ipvs nat模式下独立于iptables与conntrack的连接跟踪表和NAT机制

ipvs nat模式下独立于iptables与conntrack的连接跟踪表和NAT机制 ipvs的连接跟踪表和NAT机制 ipvs只有DNAT和de-DNAT功能 ,它独立与iptables和conntrack,实现了自己的一套连接跟踪表和NAT机制。ipvs仅仅在做DNAT后对conntrack连接进行更新,防止回包因为没有记录而被丢弃。 i…

邂逅Vue.js开发

1、认识Vue Vue (读音 /vjuː/&#xff0c;类似于 view) 是一套用于构建用户界面的渐进式 JavaScript框架。 全称是Vue.js或者Vuejs&#xff1b;它基于标准 HTML、CSS 和 JavaScript 构建&#xff0c;并提供了一套声明式的、组件化的编程模型&#xff1b;帮助你高效地开发用户…

学习笔记 一天学完JavaScript基础语法(全)

JavaScript 文章目录JavaScript第一章——初始JavaScript1.1 浏览器执行 JS 简介1.2 JS的组成1.2.1 ECMAScript1.2.2 DOM ——文档对象模型1.2.3 BOM ——浏览器对象模型1.3 JS的写法1.3.1 行内式写法1.3.2 内嵌式写法1.3.3 外部js文件1.4 注释1.5 变量1.5.1 变量声明1.5.2 变量…

【源码解析】Nacos配置中心的源码解析

POM文件添加依赖 <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency>在依赖中查看自动装配文件spring.factories org.springframework.cloud.bootstr…

String类详解

在Java编程中&#xff0c;除Object类外&#xff0c;最常用的类就是String类了。本文将从String类源码出发&#xff0c;对String类进行一个全面的分析&#xff0c;以帮忙我们更好的理解和使用String类。 String类概述 Java 使用 String 类代表字符串。Java 中的所有字符串字面…