一、特点

(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)相关推荐

  1. python内置的数据结构_Python内置数据结构

    「Python数据分析养成记」 第四篇 前言 前文讲解了Python的基础数据类型,但是对于复杂的问题,最基础的数据类型可能没法解决.例如,每个变量(容器)只能装一种饮料(雪碧或者可乐),那能否一个变 ...

  2. python的68个内置函数

    内置函数 内置函数就是python给你提供的, 拿来直接用的函数, 比如print., input等. 截止到python版本3.6.2 python一共提供了68个内置函数. #68个内置函数 # ...

  3. Python标准库:内置函数dict(mapping, **kwarg)

    Python标准库:内置函数dict(mapping, **kwarg) 本函数是从一个映射函数对象构造一个新字典. 与dict(**kwarg)函数不一样的地方是參数输入是一个映射类型的函数对象,比 ...

  4. boost::python模块实现使用内置 python 数据类型创建 ndarrays 的示例,并提取成员变量的类型和值测试程序

    boost::python模块实现使用内置 python 数据类型创建 ndarrays 的示例,并提取成员变量的类型和值测试程序 实现功能 C++实现代码 实现功能 boost::python模块实 ...

  5. 这么多的内置函数能记住吗?对python的68个内置函数分类总结!

    [阅读全文] 内置函数列表 ''' abs() dict() help() min() setattr() all() dir() hex() next() slice() any() divmod( ...

  6. python文档整理,Python官方文档内置函数整理Word版

    <Python官方文档内置函数整理Word版>由会员分享,可在线阅读,更多相关<Python官方文档内置函数整理Word版(6页珍藏版)>请在人人文库网上搜索. 1.传播优秀W ...

  7. 以下哪个不是python的内置函数_以下哪个 Python 内置函数可以返回列表对象中元素个数。...

    [多选题]假设 x=[0,1,2,3],执行哪些语句之后,x 的值为[0, 1, 2]. [多选题]以下哪些对象的分隔符为逗号. [单选题]已知列表 x=[0,1,2,1,4],那么执行语句 del ...

  8. 【Python养成】常用内置函数 — 2(详解25个内置函数)

    图片来自互联网 文章目录 前言 二.内置函数详解 1.函数:chr(x) 2.函数:dir([obj]) 3.函数:divmod(x,y) 4.函数:enumerate(sequence, [star ...

  9. power bi形状地图_如何使用内置形状图在Power BI中创建地理图

    power bi形状地图 Introduction 介绍 This is the second article of a series dedicated to discovering geograp ...

最新文章

  1. 一次项目组聚餐,让我重新认识了很多人
  2. Markdown 中画图
  3. iOS核心动画之CALayer-layer的创建
  4. 设计模式-17-迭代器
  5. spine骨骼动画基础一文通
  6. Linux开发板无法连接ssh
  7. IDEA 启动本地 Flink Web UI
  8. crmeb知识付费uniapp重构 适配小程序 APP 微信H5
  9. 导热材料在电子产品散热系统中的重要性
  10. UVA11021 Tribles 概率
  11. 04.超网_静态路由
  12. 科一科四题库技巧软件源码
  13. HTML实现图片点击放大效果
  14. JavaScript阻止链接跳转
  15. 敦煌日历2023 | 千年流光,风雅不绝
  16. 科研:中科大论文查新查引所用论文数据库
  17. 老白理解的REDO LOG
  18. 学习日记day27 平面设计 构图
  19. 一文详解从车联网到工业智联网
  20. Z-Wave Association Basics ZWAVE设备之间的本地关联

热门文章

  1. zabbix在configure时候遇到的问题(Ubuntu\debian)
  2. 话里话外:实现信息化综合集成需要经历的五个阶段
  3. 软考(5)--软件工程
  4. CISCO 2950,3550交换机的端口隔离
  5. 【计网】IP地址、子网掩码、网络号、主机号、网络地址、主机地址以及ip段/数字-如192.168.0.1/24是什么意思?
  6. Apache Common常用jar包
  7. docker镜像构建工具kaniko构建过程缓慢原因探究
  8. deepin v20.2.4设置全局搜索的快捷键
  9. 使用docker-compose配置redis服务
  10. 【收藏】deepin环境安装nodejs