网易首页 > 网易号 > 正文 申请入驻

Python 中 list 是如何实现和使用的?

0
分享至


前言

Python 中的 list 是一种序列类型,可以存储任意类型的对象,如整数、字符串、元组等。list 是可变的,也就是说,我们可以在运行时添加、删除或修改 list 中的元素。那么,Python 中的 list 是如何实现和使用的呢?本文将从以下几个方面来介绍:

  • list 的内部结构

  • list 的动态扩容机制

  • list 的时间复杂度分析

  • list 的常用操作和技巧

list 的内部结构

Python 中的 list 实际上是一个数组,但不是一个普通的数组,而是一个指针数组。也就是说,list 中的每个元素都是一个指向对象的指针,而不是对象本身。这样做的好处是,list 可以存储不同类型的对象,而不需要考虑对象的大小或对齐问题。同时,这也意味着,list 中的元素并不是连续存储的,而是分散在内存中的不同位置,通过指针来连接。

下图展示了一个包含 4 个元素的 list 的内部结构:

可以看到,list 对象本身有一个指向指针数组的指针,指针数组中的每个指针又指向一个对象。这样,我们可以通过 list 对象的指针,找到指针数组,再通过指针数组的索引,找到对应的对象。

list 的动态扩容机制

由于 list 是可变的,我们可以随时向 list 中添加或删除元素。那么,list 是如何管理指针数组的大小的呢?当指针数组的空间不足时,list 会自动申请更大的空间,并将原来的指针数组复制过去,然后释放原来的空间。同样,当指针数组的空间过剩时,list 会自动缩小空间,并将原来的指针数组复制过去,然后释放原来的空间。

它在 CPython 中的实现如下:

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 enough
to accommodate the newsize. If the newsize falls lower than half
the allocated size, then proceed with the realloc() to shrink the 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 *);
items = (PyObject **)PyMem_Realloc(self->ob_item, num_allocated_bytes);
if (items == NULL) {
PyErr_NoMemory();
return -1;
}
self->ob_item = items;
Py_SIZE(self) = newsize;
self->allocated = new_allocated;
return 0;
}

具体的扩缩容原理是:

  • 如果目标容量newsize小于等于原容量allocated,并且大于等于它的一半,那么不需要重新分配内存,只需要修改 list 的大小Py_SIZEnewsize

  • 如果newsize大于allocated,或者小于它的一半,那么需要重新分配内存,根据一个公式计算新容量new_allocated,然后使用PyMem_Realloc函数重新分配内存,并更新 list 的指针ob_item,大小Py_SIZE和容量allocated

  • 如果分配内存失败,那么抛出内存错误异常。

计算新容量的公式原理是:

  • 计算新容量new_allocated。它等于newsize加上newsize的八分之一,再加上一个常数。这个常数根据newsize的大小而变化,如果newsize小于9,那么常数是3,否则是6。

  • 检查new_allocated是否超过了PyObject指针的最大值。如果是,那么抛出内存错误异常。

  • 如果newsize是0,则将new_allocated设置为 0。

list append 示例

以 list 的append()函数为例介绍扩容过程,当 list 中有四个元素时,allocated是 4,指针数组的空间刚好够用。当我们向 list 中添加第五个元素时,指针数组的空间不足,需要扩容。根据公式,新容量为4+4*1/8+3=7.5,取整为8,list 会申请一个大小为 8 的新指针数组,并将原来的指针数组复制过去,添加新元素,并释放原来的空间。

list 的时间复杂度分析

由于 list 的内部结构和动态扩容机制,list 的不同操作的时间复杂度也不同。一般来说,我们可以根据操作的类型,将 list 的操作分为以下几类:


  • 索引操作:通过索引访问或修改 list 中的元素,如lst[i]lst[i] = x。这类操作的时间复杂度是O(1),也就是常数时间,因为我们只需要通过 list 对象的指针,找到指针数组,再通过索引,找到对应的对象,这些操作都是固定的,和 list 的大小无关。



  • 追加操作:在 list 的末尾添加一个元素,如lst.append(x)。这类操作的时间复杂度是O(1),也就是常数时间,因为我们只需要在指针数组的末尾,添加一个指向对象的指针,这个操作也是固定的,和 list 的大小无关。当然,如果指针数组的空间不足,需要扩容,那么这个操作的时间复杂度就会变成O(n),也就是线性时间,因为我们需要申请一个新的指针数组,并将原来的指针数组复制过去,这些操作都和 list 的大小成正比。但是,由于 list 的增长因子的存在,扩容的次数是有限的,而且随着 list 的大小的增加,扩容的频率会降低,所以我们可以认为,追加操作的平均时间复杂度仍然是O(1),也就是常数时间。



  • 插入操作:在 list 的任意位置插入一个元素,如lst.insert(i, x)。这类操作的时间复杂度是O(n),也就是线性时间,因为我们需要在指针数组的指定位置,插入一个指向对象的指针,这意味着,我们需要将该位置之后的所有指针,都向后移动一位,这些操作都和 list 的大小成正比。当然,如果指针数组的空间不足,需要扩容,那么这个操作的时间复杂度就会变成O(n),也就是线性时间,因为我们需要申请一个新的指针数组,并将原来的指针数组复制过去,这些操作都和 list 的大小成正比。所以,插入操作的平均时间复杂度仍然是O(n),也就是线性时间。



  • 删除操作:从 list 的任意位置删除一个元素,如lst.pop(i)del lst[i]。这类操作的时间复杂度也是O(n),也就是线性时间,因为我们需要在指针数组的指定位置,删除一个指向对象的指针,这意味着,我们需要将该位置之后的所有指针,都向前移动一位,这些操作都和 list 的大小成正比。当然,如果指针数组的空间过剩,需要缩容,那么这个操作的时间复杂度也会变成O(n),也就是线性时间,因为我们需要申请一个新的指针数组,并将原来的指针数组复制过去,这些操作都和 list 的大小成正比。所以,删除操作的平均时间复杂度仍然是O(n),也就是线性时间。



  • 遍历操作:遍历 list 中的所有元素,如for x in lstlist(lst)。这类操作的时间复杂度也是O(n),也就是线性时间,因为我们需要访问指针数组中的每个指针,再通过指针访问对应的对象,这些操作都和 list 的大小成正比。


从上面的分析可以看出,list 的不同操作的时间复杂度有所不同。一般来说,索引和追加操作是最快的,插入和删除操作是最慢的,遍历操作是中等的。所以,当我们使用 list 时,应该尽量避免在 list 的中间或开头插入或删除元素,而是尽量在 list 的末尾追加或删除元素,这样可以提高 list 的性能。

list 的常用操作和技巧

除了上面介绍的基本操作,list 还有一些常用的操作和技巧,可以让我们更方便地使用 list 。下面列举了一些常用的操作和技巧,以及它们的示例代码:

  • 切片操作:通过指定起始和结束的索引,以及可选的步长,来获取 list 的一部分,如lst[start:end:step]。这个操作会返回一个新的 list ,不会修改原来的 list 。示例代码:

lst = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
sub_lst = lst[2:8:2] # 从索引 2 开始,到索引 8 结束,步长为 2,获取 list 的一部分
print(sub_lst) # [3, 5, 7]
  • 列表推导式:通过一行代码,根据一个已有的 list,生成一个新的 list,如[x * 2 for x in lst]。这个操作可以让我们更简洁地创建 list,而不需要使用循环或追加操作。示例代码:

lst = [1, 2, 3, 4, 5]
new_lst = [x * 2 for x in lst] # 根据 lst 中的每个元素,生成一个新的 list,每个元素都乘以 2
print(new_lst) # [2, 4, 6, 8, 10]
  • 排序操作:通过指定一个排序规则,来对 list 中的元素进行排序,如lst.sort(key=lambda x: x[1]),key 选填。这个操作会修改原来的 list ,如果不想修改原来的 list ,可以使用sorted(lst, key=lambda x: x[1]),这个操作会返回一个新的 list 。示例代码:

lst = [(1, 3), (2, 5), (3, 4), (4, 2), (5, 1)]
lst.sort(key=lambda x: x[1]) # 根据 lst 中的每个元素的第二个值,对 list 进行排序
print(lst) # [(5, 1), (4, 2), (1, 3), (3, 4), (2, 5)]
  • 反转操作:通过指定一个反转标志,来对 list 中的元素进行反转,如lst.reverse()。这个操作会修改原来的 list,如果不想修改原来的 list ,可以使用reversed(lst),这个操作会返回一个反转的迭代器。示例代码:

lst = [1, 2, 3, 4, 5]
lst.reverse() # 反转 list 中的元素
print(lst) # [5, 4, 3, 2, 1]
  • 拼接操作:通过使用加号或乘号,来对 list 进行拼接,如lst1 + lst2lst * 3。这个操作会返回一个新的 list,不会修改原来的 list。示例代码:

lst1 = [1, 2, 3]
lst2 = [4, 5, 6]
new_lst = lst1 + lst2 # 将 lst1 和 lst2 拼接成一个新的 list
print(new_lst) # [1, 2, 3, 4, 5, 6]
new_lst = lst1 * 3 # 将 lst1 重复三次,拼接成一个新的 list
print(new_lst) # [1, 2, 3, 1, 2, 3, 1, 2, 3]
  • 拆分操作:通过使用星号,来对 list 进行拆分,如x, *y, z = lst。这个操作可以让我们更方便地获取 list 中的某些元素,而不需要使用索引或切片。示例代码:

lst = [1, 2, 3, 4, 5]
x, *y, z = lst # 将 lst 中的第一个元素赋值给 x,最后一个元素赋值给 z,中间的元素赋值给 y
print(x) # 1
print(y) # [2, 3, 4]
print(z) # 5
总结

list 是 Python 中最常用的数据结构之一,了解它的内部结构和动态扩容机制,可以让我们更好地理解和使用 list 。同时,掌握 list 的时间复杂度分析,可以让我们更高效地编写代码。最后,熟练 list 的常用操作和技巧,可以让我们更简洁和优雅地处理数据。

Python猫技术交流群开放啦!群里既有国内一二线大厂在职员工,也有国内外高校在读学生,既有十多年码龄的编程老鸟,也有中小学刚刚入门的新人,学习氛围良好!想入群的同学,请在公号内回复『交流群』,获取猫哥的微信 (谢绝广告党,非诚勿扰!)~

还不过瘾?试试它们

如果你觉得本文有帮助

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
刚上映就爆了!《扫恶》上映拿下 9.1分,大尺度案件全程无尿点

刚上映就爆了!《扫恶》上映拿下 9.1分,大尺度案件全程无尿点

糊咖娱乐
2026-03-20 14:56:27
3月30日起执行!不用再买墓地,国家正式放开殡葬新选择

3月30日起执行!不用再买墓地,国家正式放开殡葬新选择

福建平子
2026-03-22 08:11:56
价格大跳水!许多人爱吃,正大量上市

价格大跳水!许多人爱吃,正大量上市

杭州之声
2026-03-20 18:49:35
名门千金海清:坐拥9500平大院,给欧豪下跪,儿子被称小谷爱凌

名门千金海清:坐拥9500平大院,给欧豪下跪,儿子被称小谷爱凌

边城少爷
2026-03-21 15:28:36
杜兰特敬服阿门绝杀,多名美记直呼见证历史,杜兰特选择谦虚

杜兰特敬服阿门绝杀,多名美记直呼见证历史,杜兰特选择谦虚

吴朑爱游泳
2026-03-22 15:25:25
舅舅突然来电你表弟撞了豪车,要赔110万,我冷静回应

舅舅突然来电你表弟撞了豪车,要赔110万,我冷静回应

王二哥老搞笑
2026-03-22 13:05:05
地主少爷当上军区副参谋长,评衔时毛主席看中将名单:怎没见他?

地主少爷当上军区副参谋长,评衔时毛主席看中将名单:怎没见他?

浩渺青史
2026-03-20 18:34:30
击落3架美军F-15E的科威特飞行员,因多项罪名已被逮捕

击落3架美军F-15E的科威特飞行员,因多项罪名已被逮捕

碳基生物关怀组织
2026-03-17 22:35:07
家族业力毁了几代人幸福,35岁女子三姐妹全离异,年入百万照样分

家族业力毁了几代人幸福,35岁女子三姐妹全离异,年入百万照样分

青梅侃史啊
2026-03-20 22:12:20
33年来第1次4连败,切尔西的首要问题是罗塞尼尔

33年来第1次4连败,切尔西的首要问题是罗塞尼尔

米奇兔
2026-03-22 18:21:48
韩媒:中国欠特朗普一声谢谢!若不是美国打压,中国芯不会那么强

韩媒:中国欠特朗普一声谢谢!若不是美国打压,中国芯不会那么强

心也简单
2026-03-22 16:57:03
《隐身的名字》大结局:文毓秀一生被毁,葛文君遭报应,柏庶坐牢

《隐身的名字》大结局:文毓秀一生被毁,葛文君遭报应,柏庶坐牢

草莓解说体育
2026-03-22 09:57:00
抛弃印度,普京急建新三角?关键时刻,中方摊牌:不解决死穴免谈

抛弃印度,普京急建新三角?关键时刻,中方摊牌:不解决死穴免谈

万国明信片
2026-03-22 17:00:28
美暂时解除石油制裁,伊朗斥“心理战”

美暂时解除石油制裁,伊朗斥“心理战”

参考消息
2026-03-21 16:59:17
浙江55岁母亲发帖:儿子不婚不育,我也想通了!告别催婚内耗!

浙江55岁母亲发帖:儿子不婚不育,我也想通了!告别催婚内耗!

川渝视觉
2026-03-21 21:43:14
生育大局已定:如不出意外,2026年起中国人口将迎来3大变化

生育大局已定:如不出意外,2026年起中国人口将迎来3大变化

蜉蝣说
2026-03-17 15:58:31
表面谦谦君子,实则流氓头子,这四位男星表里不一

表面谦谦君子,实则流氓头子,这四位男星表里不一

看尽落尘花q
2026-02-19 19:28:49
“梅姨”现身并落网!对贩卖儿童事实供认不讳,已被依法逮捕

“梅姨”现身并落网!对贩卖儿童事实供认不讳,已被依法逮捕

南方都市报
2026-03-21 11:35:00
王鸥直播甩“单身炸弹”!五年“德云绯闻”灰飞烟灭

王鸥直播甩“单身炸弹”!五年“德云绯闻”灰飞烟灭

圆梦的小老头
2026-03-22 18:29:15
伊朗退了,叙利亚退了,巴勒斯坦退了,黎巴嫩退了,塞尔维亚退了

伊朗退了,叙利亚退了,巴勒斯坦退了,黎巴嫩退了,塞尔维亚退了

南权先生
2026-01-29 15:57:27
2026-03-22 19:03:00
Python猫 incentive-icons
Python猫
人生苦短,我用Python。博客:https://pythoncat.top
729文章数 8121关注度
往期回顾 全部

科技要闻

嫌台积电太慢 马斯克要把芯片产能飙升50倍

头条要闻

白宫发布高市早苗访美照片神态夸张 日本网友:耻辱

头条要闻

白宫发布高市早苗访美照片神态夸张 日本网友:耻辱

体育要闻

郑钦文连续迎战大满贯冠军 “双教练”团队正式亮相

娱乐要闻

今晚首播!央视年代剧《冬去春来》来了

财经要闻

睡梦中欠债1.2万?这只“虾”杀疯了

汽车要闻

14.28万元起 吉利银河星耀8远航家开启预售

态度原创

艺术
房产
亲子
健康
数码

艺术要闻

国内仅存的颜真卿墨迹,没见过比它更美的字

房产要闻

全城狂送1000杯咖啡!网易房产【早C计划】,即刻启动!

亲子要闻

宝蓝的奶奶生病了,宝蓝帮助奶奶收拾房间,清扫地面,收拾厨房~

转头就晕的耳石症,能开车上班吗?

数码要闻

飞利浦复古耳机来了,配色亮了

无障碍浏览 进入关怀版