python 3.8.2 / 内置的数据结构 / list (类似于 STL 中的 vector)
一、特点
(1)相对于 tuple 来说,list 是动态的(mutable),即:各个元素都是可变的。
(2)可以通过索引进行查询。
(3)list 中的元素可以是 python 中的任何对象。例如:list、tuple、dict、set、字符串和整数,并且可以任意混合。
(4)所有元素由一个中括号“[ ]”包裹。
二、相关操作
1、增
a、append() ,在 list 尾部插入元素。
b、insert(),在 list 的指定位置插入元素。
2、删
a、pop(),弹出 list 尾部元素并返回,与 append 对应。
b、remove(),删除指定元素。
c、del ,删除指定的位置的元素。
3、改
直接用“=”修改指定元素即可。
4、查
类似于数组的索引。
栗子:
if __name__ == '__main__':testlist = ['one', 'two', 'six']# 增# append 在 list 末尾添加元素。testlist.append('three')# insert 在指定的位置插入元素。testlist.insert(1, 'four')# 删# pop 删除 list 尾部元素。t = testlist.pop()# remove 删除 list 中的元素。testlist.remove('two')# del 删除 list 中指定范围的元素。del testlist[0:2]# 改testlist[0] = 'hello world!'
三、实现原理
1、底层的实现方式是 PyObject 类型的二维指针数组 。在 python 世界中,一切都是对象,无论是 int 、string 还是 list 等,这些都继承于 PyObject ,所以 list 保存的是各个 PyObject 的指针,传指针相对于传对象效率更高。
2、空 list 的大小是 40B,即:一个描述 list 的结构体的大小。该结构体如下所示:
typedef struct {PyObject_VAR_HEAD // 用来保存已使用的内存槽的数量。PyObject **ob_item; // 用来保存对象的指针的指针数组。Py_ssize_t allocated; // 预先分配的内存槽的总容量,即:ob_item 数组的容量。
} PyListObject;
3、当一个空的 list 执行 append 时,list 实际上会多分配一些内存,这样可以提高下次 append 的高效性,即:时间复杂度为 O(1)。分配内存的算法如下:
new_allocated = (size_t)newsize + (newsize >> 3) + (newsize < 9 ? 3 : 6);
总体效果是随着对象的数量增多,单次分配的内存槽逐渐变大。
4、append() 源码实现
(1)过程简述
- 判断是否需要重新对 list 的内存槽数组(ob_item)进行重新分配。若需要,则申请新的内存槽数组、将旧内存槽数组中的数据拷贝到新的内存槽数组中,释放旧的内存槽数组。
- 将新对象的指针加入到 ob_item 的尾部。
(2)append() 函数实际上调用的是 app1() 函数。
/*** 向 list 的尾部添加对象。
*/
static PyObject *list_append(PyListObject *self, PyObject *object)
{if (app1(self, object) == 0)Py_RETURN_NONE;return NULL;
}
(3)app1 函数的执行过程如下:
static int app1(PyListObject *self, PyObject *v)
{/*** 返回当前 list 中对象的数量。*/Py_ssize_t n = PyList_GET_SIZE(self);assert(v != NULL);if (n == PY_SSIZE_T_MAX){PyErr_SetString(PyExc_OverflowError,"cannot add more objects to list");return -1;}/*** 重新调整 list 的内存。* 创建新的内存槽数组、释放旧的内存槽数组。*/if (list_resize(self, n + 1) < 0)return -1;/*** 对象 v 的引用计数 + 1 。*/Py_INCREF(v);/*** 将对象指针插入到 list 的 ob_item 指针数据中。*/PyList_SET_ITEM(self, n, v);return 0;
}
(4)精髓就在 list_resize() 函数中了,该函数完成了内存的重新分配。
static int list_resize(PyListObject *self, Py_ssize_t newsize)
{PyObject **items;size_t new_allocated, num_allocated_bytes;Py_ssize_t allocated = self->allocated;/* Bypass realloc() when a previous overallocation is large enoughto accommodate the newsize. If the newsize falls lower than halfthe allocated size, then proceed with the realloc() to shrink the list.*//*** (1)若 newsize < 已分配的内存槽的数量的 1/2,则重新分配内存槽数组(缩容)。* (2)若 newsize >= 已分配的内存槽的数量的 1/2,且 newsize =< 已分配的内存槽的数量,则无需调整。* (3)若 newsize > 已分配的内存槽的数量,则重新分配内存槽数组(扩容)。* (注意,新的 list 和旧的 list 不在同一个内存起始地址。)*/if (allocated >= newsize && newsize >= (allocated >> 1)){assert(self->ob_item != NULL || newsize == 0);Py_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().* The growth pattern is: 0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...* 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.*//*** 内存分配方案,获取新的内存槽的数量。*/new_allocated = (size_t)newsize + (newsize >> 3) + (newsize < 9 ? 3 : 6);if (new_allocated > (size_t)PY_SSIZE_T_MAX / sizeof(PyObject *)){PyErr_NoMemory();return -1;}if (newsize == 0)new_allocated = 0;/*** 新的内存槽数组的字节总数。*/num_allocated_bytes = new_allocated * sizeof(PyObject *);/*** (1)创建新的内存槽数组;* (2)将旧内存槽数组中的数据拷贝到新的内存槽数组中;* (3)释放旧的内存槽数组。* obmalloc.c PyMem_realloc() 函数。*/items = (PyObject **)PyMem_Realloc(self->ob_item, num_allocated_bytes);if (items == NULL){PyErr_NoMemory();return -1;}/*** 将新的状态更新到 list 对象中。*/self->ob_item = items;Py_SIZE(self) = newsize;self->allocated = new_allocated;return 0;
}
5、insert() 源码实现
(1)过程简述
- 判断是否需要重新对 list 的内存槽数组(ob_item)进行重新分配。若需要,则申请新的内存槽数组、将旧内存槽数组中的数据拷贝到新的内存槽数组中,释放旧的内存槽数组。
- 将 where 之后的元素全部向后挪一位。
- 将 new item 放到 where 的位置上。
(2)append() 函数实际上调用的是 ins1() 函数。
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);
}
(3)ins1() 源码
static int ins1(PyListObject *self, Py_ssize_t where, PyObject *v)
{/*** 返回当前已用的内存槽的数量,即:list 中已有的元素的数量。*/Py_ssize_t i, n = Py_SIZE(self);PyObject **items;if (v == NULL){PyErr_BadInternalCall();return -1;}if (n == PY_SSIZE_T_MAX){PyErr_SetString(PyExc_OverflowError,"cannot add more objects to list");return -1;}/*** 调整内存槽数组。*/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;/*** 将 where 之后的数据向后挪一位。*/for (i = n; --i >= where;)items[i + 1] = items[i];Py_INCREF(v);items[where] = v;return 0;
}
四、拓展
下面是两种创建空 list 的方法,哪一个效率更高呢?
empty_list = []
empty_list = list()
答案是前者效率更高,因为前者是内置的C函数,可以直接调用;后者是 python 的函数调用,会创建 stack 和参数检查,比较浪费时间。
(SAW:Game Over!)
python 3.8.2 / 内置的数据结构 / list (类似于 STL 中的 vector)相关推荐
- python内置的数据结构_Python内置数据结构
「Python数据分析养成记」 第四篇 前言 前文讲解了Python的基础数据类型,但是对于复杂的问题,最基础的数据类型可能没法解决.例如,每个变量(容器)只能装一种饮料(雪碧或者可乐),那能否一个变 ...
- python的68个内置函数
内置函数 内置函数就是python给你提供的, 拿来直接用的函数, 比如print., input等. 截止到python版本3.6.2 python一共提供了68个内置函数. #68个内置函数 # ...
- Python标准库:内置函数dict(mapping, **kwarg)
Python标准库:内置函数dict(mapping, **kwarg) 本函数是从一个映射函数对象构造一个新字典. 与dict(**kwarg)函数不一样的地方是參数输入是一个映射类型的函数对象,比 ...
- boost::python模块实现使用内置 python 数据类型创建 ndarrays 的示例,并提取成员变量的类型和值测试程序
boost::python模块实现使用内置 python 数据类型创建 ndarrays 的示例,并提取成员变量的类型和值测试程序 实现功能 C++实现代码 实现功能 boost::python模块实 ...
- 这么多的内置函数能记住吗?对python的68个内置函数分类总结!
[阅读全文] 内置函数列表 ''' abs() dict() help() min() setattr() all() dir() hex() next() slice() any() divmod( ...
- python文档整理,Python官方文档内置函数整理Word版
<Python官方文档内置函数整理Word版>由会员分享,可在线阅读,更多相关<Python官方文档内置函数整理Word版(6页珍藏版)>请在人人文库网上搜索. 1.传播优秀W ...
- 以下哪个不是python的内置函数_以下哪个 Python 内置函数可以返回列表对象中元素个数。...
[多选题]假设 x=[0,1,2,3],执行哪些语句之后,x 的值为[0, 1, 2]. [多选题]以下哪些对象的分隔符为逗号. [单选题]已知列表 x=[0,1,2,1,4],那么执行语句 del ...
- 【Python养成】常用内置函数 — 2(详解25个内置函数)
图片来自互联网 文章目录 前言 二.内置函数详解 1.函数:chr(x) 2.函数:dir([obj]) 3.函数:divmod(x,y) 4.函数:enumerate(sequence, [star ...
- power bi形状地图_如何使用内置形状图在Power BI中创建地理图
power bi形状地图 Introduction 介绍 This is the second article of a series dedicated to discovering geograp ...
最新文章
- 一次项目组聚餐,让我重新认识了很多人
- Markdown 中画图
- iOS核心动画之CALayer-layer的创建
- 设计模式-17-迭代器
- spine骨骼动画基础一文通
- Linux开发板无法连接ssh
- IDEA 启动本地 Flink Web UI
- crmeb知识付费uniapp重构 适配小程序 APP 微信H5
- 导热材料在电子产品散热系统中的重要性
- UVA11021 Tribles 概率
- 04.超网_静态路由
- 科一科四题库技巧软件源码
- HTML实现图片点击放大效果
- JavaScript阻止链接跳转
- 敦煌日历2023 | 千年流光,风雅不绝
- 科研:中科大论文查新查引所用论文数据库
- 老白理解的REDO LOG
- 学习日记day27 平面设计 构图
- 一文详解从车联网到工业智联网
- Z-Wave Association Basics ZWAVE设备之间的本地关联
热门文章
- zabbix在configure时候遇到的问题(Ubuntu\debian)
- 话里话外:实现信息化综合集成需要经历的五个阶段
- 软考(5)--软件工程
- CISCO 2950,3550交换机的端口隔离
- 【计网】IP地址、子网掩码、网络号、主机号、网络地址、主机地址以及ip段/数字-如192.168.0.1/24是什么意思?
- Apache Common常用jar包
- docker镜像构建工具kaniko构建过程缓慢原因探究
- deepin v20.2.4设置全局搜索的快捷键
- 使用docker-compose配置redis服务
- 【收藏】deepin环境安装nodejs