list对象_list对象,容量自适应的数组式容器

本文探讨了Python内置容器list对象,分析了其基本操作如append和insert的时间复杂度和空间复杂度。特别是指出,insert方法在头部插入元素时性能较差,而append方法在尾部添加元素则更为高效。文章还揭示了list对象的内部结构,包括PyListObject结构体和动态数组的使用,以及list对象如何根据元素数量自动扩缩容。最后,提到了列表尾部操作的高效性和其他相关操作,如动态扩容和浅拷贝。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

点击上方蓝色小字 “小菜学编程” ,关注我们?

笔者经常在面试中与候选人探讨 Python 内置容器对象, list 作为最常用容器中的一员,肯定少不了它:

  • 你用过 list 对象吗?
  • list 对象都支持哪些操作?时间复杂度、空间复杂度分别是多少?
  • 试分析 appendinsert 这两个方法的时间复杂度?
  • 头部追加性能较差,如何解决?

list 对象的基本用法和技术原理,每个 Python 开发工程师都必须掌握,但不少候选人对此却只是一知半解。本节,我们一起查缺补漏,力求彻底掌握 list 对象,完美解答面试官针对 list 对象的灵魂之问。

基本用法

我们先来回顾一下 list 对象的基本操作:

# 新建一个列表
>>> l = [1, 2, 3]
>>> l
[1, 2, 3]

# 向尾部追加元素,列表对象视情况自动扩容
>>> l.append(4)
>>> l
[1, 2, 3, 4]

# 从尾部弹出元素,列表对象视情况自动缩容
>>> l.pop()
4
>>> l
[1, 2, 3]

# 向头部插入元素,该操作需要挪动后面的元素,谨慎使用!
>>> l.insert(0, 4)
>>> l
[4, 1, 2, 3]

# 从头部弹出元素,该操作需要挪动后面的元素,谨慎使用!
>>> l.pop(0)
4
>>> l
[1, 2, 3]

# 查找元素第一次出现位置的下标
>>> l.index(2)
1

# 用一个可迭代对象扩展列表——元素逐一追加到尾部
>>> l.extend([1, 2])
>>> l
[1, 2, 3, 1, 2]

# 计算元素出现的个数
>>> l.count(1)
2
>>> l.count(3)
1

# 将列表反转
>>> l.reverse()
>>> l
[2, 1, 3, 2, 1]

# 将列表清空
>>> l.clear()
>>> l
[]

一个合格的 Python 开发工程师,除了必须熟练掌握 list 对象的基本操作,还需要对每个操作的 实现原理 及对应的 时间复杂度 、 空间复杂度 有准确的认识。列表操作总体比较简单,但有个操作特别容易被误用:

  • insert 方法向头部追加元素时需要挪动整个列表,时间复杂度是  ,性能极差,需谨慎使用;
  • append 方法向尾部追加元素时,无需挪动任何元素,时间复杂度是  ;
  • pop 方法从头部弹出元素时也需要挪动整个列表,时间复杂度是  ,同样需谨慎使用;
  • pop 方法从尾部弹出元素时,无需挪动任何元素,时间复杂度是  ;

由此可见,对列表头部和尾部进行操作,性能有天壤之别。后续我们将一起探索 list 对象内部结构,从中寻找造成这种现象的原因。此外, list 对象还可根据元素个数 自动扩缩容 ,其中秘密也将一一揭晓。

内部结构

list 对象在 Python 内部,由 PyListObject 结构体表示,定义于头文件 Include/listobject.h 中:

typedef struct {
    PyObject_VAR_HEAD
    /* Vector of pointers to list elements.  list[0] is ob_item[0], etc. */
    PyObject **ob_item;

    /* ob_item contains space for 'allocated' elements.  The number
     * currently in use is ob_size.
     * Invariants:
     *     0 <= ob_size <= allocated
     *     len(list) == ob_size
     *     ob_item == NULL implies ob_size == allocated == 0
     * list.sort() temporarily sets allocated to -1 to detect mutations.
     *
     * Items must normally not be NULL, except during construction when
     * the list is not yet visible outside the function that builds it.
     */
    Py_ssize_t allocated;
} PyListObject;

毫无疑问, list 对象是一种 变长对象 ,因此包含变长对象公共头部。除了公共头部, list 内部维护了一个动态数组,而数组则依次保存元素对象的指针:

  • ob_item ,指向动态数组的指针,动态数组保存元素对象的指针;
  • allocated ,动态数组长度,即列表 容量 ;
  • ob_size ,动态数组当前保存元素个数,即列表 长度 ;
dd30452107eb423611783e2e2e6f20bf.png

尾部操作

在列表对象尾部增删元素,可快速完成,无须挪动其他元素。

假设列表元素 l 内部数组长度为 5 ,以及保存 3 个元素,分别是:1 、 2 、 3 。当我们调用 append 方法向尾部追加元素时,由于内部数组还有未用满,只需将新元素保存于数组下一可用位置并更新 ob_size 字段:

8cc45e56c90a75fe301e370d12bd1c68.png

因此,大多数情况下, append 方法性能都足够好,时间复杂度是  。

list 对象除了尾部操作外,还有 动态扩容头部操作元素 以及 浅拷贝 等常用方法,这些你又掌握了多少呢?点击 “

?  原创不易,求赞,求在看

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值