Skip to content

01-列表

本章代码基于 Python 3.11+ 编写

列表是 Python 最常用的数据结构,用于存储有序的可变元素集合。


本章全面讲解 Python 列表的创建、访问、修改、复制和高级用法。


概念铺垫

为什么需要列表?一个真实的批量数据场景

问题场景: 你在开发一个学生成绩管理系统,需要记录多个学生的成绩。

不使用列表的麻烦:

python
# 每个成绩一个变量
score1 = 85
score2 = 92
score3 = 78
score4 = 96
score5 = 88

# 计算平均分?需要手动写所有变量
average = (score1 + score2 + score3 + score4 + score5) / 5

问题:

  • 如果有100个学生,需要100个变量
  • 如何批量处理?很难!

使用列表的解决方案:

python
# 用一个列表存储所有成绩
scores: list[int] = [85, 92, 78, 96, 88]

# 一行代码计算平均分
average: float = sum(scores) / len(scores)
print(f"平均分:{average}")

这就是列表的价值:用一个容器存储多个相关数据


列表解决了什么问题?

列表的本质是:用一个名字,管理多个数据

就像你用一个"购物清单"记录多个物品,而不是每样东西写一张纸条。

列表的优势:

  1. 批量管理:不用为每个数据创建单独的变量
  2. 动态调整:随时添加、删除、修改元素
  3. 统一处理:可以用循环处理所有元素
  4. 有序访问:通过索引快速找到任何元素

在Python中,列表是最常用的数据结构,几乎每个程序都会用到。


L1 理解层:会用

列表的最简用法(3分钟上手)

列表的核心操作:创建、访问、修改。

python
# 创建列表
fruits: list[str] = ["苹果", "香蕉", "橘子"]

# 访问元素
print(fruits[0])  # 苹果

# 修改元素
fruits[0] = "西瓜"
print(fruits)  # ["西瓜", "香蕉", "橘子"]

# 添加元素
fruits.append("葡萄")
print(fruits)  # ["西瓜", "香蕉", "橘子", "葡萄"]

这就是列表的基本用法。接下来我们详细学习列表的所有功能。


第一部分:列表基础

什么是列表

列表(List) 是 Python 中最常用的数据结构,用于存储有序的元素集合。

┌─────────────────────────────────────────────────────────────┐
│                    列表的特点                                │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   ✅ 有序:元素有固定的顺序,可通过索引访问                 │
│   ✅ 可变:可以修改、添加、删除元素                         │
│   ✅ 可重复:允许有相同的元素                               │
│   ✅ 异构:可以存储不同类型的元素                           │
│   ✅ 动态:大小可变,自动调整内存                           │
│                                                             │
│   表示方法:使用方括号 []                                    │
│   例如:[1, 2, 3, 4, 5]                                     │
│                                                             │
│   底层实现:动态数组                                         │
│   • 连续内存存储                                            │
│   • O(1) 索引访问                                           │
│   • O(n) 插入/删除(中间位置)                              │
│                                                             │
└─────────────────────────────────────────────────────────────┘

创建列表

python
# 方式 1:使用方括号
fruits = ["apple", "banana", "orange"]
numbers = [1, 2, 3, 4, 5]
mixed = [1, "hello", 3.14, True, None]  # 异构列表

# 方式 2:创建空列表
empty_list = []
empty_list2 = list()

# 方式 3:从其他可迭代对象创建
from_string = list("hello")    # ['h', 'e', 'l', 'l', 'o']
from_range = list(range(5))    # [0, 1, 2, 3, 4]
from_tuple = list((1, 2, 3))   # [1, 2, 3]

# 方式 4:列表推导式
squares = [i ** 2 for i in range(1, 6)]  # [1, 4, 9, 16, 25]

# 方式 5:重复创建
zeros = [0] * 5        # [0, 0, 0, 0, 0]
matrix = [[0] * 3 for _ in range(3)]  # 3x3 矩阵

索引访问

python
fruits = ["apple", "banana", "orange", "grape", "mango"]

# 正向索引(从 0 开始)
print(fruits[0])   # apple(第一个)
print(fruits[1])   # banana
print(fruits[4])   # mango(最后一个)

# 反向索引(从 -1 开始)
print(fruits[-1])  # mango(最后一个)
print(fruits[-2])  # grape(倒数第二个)
print(fruits[-5])  # apple(第一个)

# 索引越界
# fruits[5]   # IndexError: list index out of range
# fruits[-6]  # IndexError: list index out of range

索引示意图:

┌─────────────────────────────────────────────────────────────┐
│                    列表索引示意图                             │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   列表:["apple", "banana", "orange", "grape", "mango"]     │
│                                                             │
│   正向索引:                                                 │
│      0        1         2        3        4                  │
│   ┌────────┬────────┬────────┬────────┬────────┐           │
│   │ apple  │ banana │ orange │ grape  │ mango  │           │
│   └────────┴────────┴────────┴────────┴────────┘           │
│                                                             │
│   反向索引:                                                 │
│     -5       -4        -3       -2       -1                  │
│                                                             │
└─────────────────────────────────────────────────────────────┘

列表切片

切片: 从列表中提取一部分元素,返回新列表。

语法结构:
┌─────────────────────────────────────────────────────────────┐
│  切片语法                                                     │
│                                                              │
│  list[start:end:step]                                        │
│       ↑    ↑    ↑                                            │
│       起始  结束  步长                                         │
│                                                              │
│  ⚠️ 注意:                                                   │
│  • end 不包含(切片到 end-1)                                 │
│  • start/end 默认:从头到尾                                  │
│  • step 默认:1                                              │
│                                                              │
│  示例:numbers[2:5]                                          │
│        从索引2到索引4(不包含5)                              │
│        结果:[2, 3, 4]                                        │
└─────────────────────────────────────────────────────────────┘

最简示例

python
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

# 基本切片
print(numbers[2:5])     # [2, 3, 4]

关键代码解释

切片含义结果
numbers[2:5]从索引2到4[2, 3, 4]
numbers[:5]从头到索引4[0, 1, 2, 3, 4]
numbers[5:]从索引5到尾[5, 6, 7, 8, 9]
numbers[:]完整复制[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

详细示例

python
# 步长切片
print(numbers[::2])     # [0, 2, 4, 6, 8](每隔一个)
print(numbers[1::2])    # [1, 3, 5, 7, 9](奇数位置)
print(numbers[::3])     # [0, 3, 6, 9](每隔两个)

# 切片赋值
numbers[2:5] = [20, 30, 40]
print(numbers)  # [0, 1, 20, 30, 40, 5, 6, 7, 8, 9]

反向切片(核心)

反向切片: step 为负数时,从右向左切片。

反向切片规则:
┌─────────────────────────────────────────────────────────────┐
│  反向切片:step < 0                                           │
│                                                              │
│  list[start:end:-1]                                          │
│       ↑    ↑    ↑                                            │
│       起始  结束  从右向左                                     │
│                                                              │
│  ⚠️ 注意:                                                   │
│  • start 必须大于 end                                        │
│  • 从 start 向 end 方向切片                                   │
│  • end 不包含                                                │
│                                                              │
│  示例:numbers[5:2:-1]                                        │
│        从索引5向索引3切片(不包含2)                           │
│        结果:[5, 4, 3]                                        │
└─────────────────────────────────────────────────────────────┘

最简示例

python
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

# 反转列表
print(numbers[::-1])    # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

关键代码解释

切片含义结果
numbers[::-1]step=-1,反转[9, 8, ..., 0]
numbers[5:2:-1]从5向3切片[5, 4, 3]
numbers[-1:-5:-1]从尾向倒数第5[9, 8, 7, 6]

详细示例

python
# 反向切片
print(numbers[::-1])    # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0](反转)
print(numbers[5:2:-1])  # [5, 4, 3](反向取)
print(numbers[-1:-5:-1])# [9, 8, 7, 6]

第二部分:列表方法

添加元素

添加方法: 在列表中插入新元素。

添加方法对比:
┌─────────────────────────────────────────────────────────────┐
│  添加元素方法                                                 │
│                                                              │
│  append(x)      在末尾添加单个元素                           │
│  insert(i, x)   在索引 i 处插入元素                          │
│  extend(iter)   在末尾添加多个元素                           │
│  + 运算符       合并列表(创建新列表)                        │
│                                                              │
│  ⚠️ append vs extend 区别:                                  │
│  append([4,5]) → 添加整个列表作为元素                        │
│  extend([4,5]) → 添加列表中的每个元素                        │
└─────────────────────────────────────────────────────────────┘

最简示例

python
fruits = ["apple", "banana"]
fruits.append("orange")
print(fruits)  # ['apple', 'banana', 'orange']

关键代码解释

方法作用示例
append(x)末尾添加单个fruits.append("orange")
insert(i, x)指定位置插入fruits.insert(1, "apricot")
extend(iter)末尾添加多个fruits.extend(["mango", "peach"])

详细示例

python
fruits = ["apple", "banana"]

# insert() - 在指定位置插入
fruits.insert(1, "apricot")  # 在索引 1 处插入
print(fruits)  # ['apple', 'apricot', 'banana', 'orange']

# extend() - 在末尾添加多个元素
fruits.extend(["mango", "peach"])
print(fruits)  # ['apple', 'apricot', 'banana', 'orange', 'mango', 'peach']

# + 运算符 - 合并列表(创建新列表)
new_fruits = fruits + ["grape"]
print(new_fruits)  # 包含 grape

append vs extend(核心)

区别: append 添加整个对象,extend 添加每个元素。

append vs extend:
┌─────────────────────────────────────────────────────────────┐
│  append vs extend 区别                                        │
│                                                              │
│  append([4, 5])                                              │
│  ┌──────────────────────────────────────┐                   │
│  │ 原列表: [1, 2, 3]                     │                   │
│  │ 结果:   [1, 2, 3, [4, 5]]             │ ← 整个列表作为元素│
│  └──────────────────────────────────────┘                   │
│                                                              │
│  extend([4, 5])                                              │
│  ┌──────────────────────────────────────┐                   │
│  │ 原列表: [1, 2, 3]                     │                   │
│  │ 结果:   [1, 2, 3, 4, 5]               │ ← 添加每个元素    │
│  └──────────────────────────────────────┘                   │
└─────────────────────────────────────────────────────────────┘

最简示例

python
list1 = [1, 2, 3]

list1.append([4, 5])
print(list1)  # [1, 2, 3, [4, 5]](添加整个列表)

list2 = [1, 2, 3]
list2.extend([4, 5])
print(list2)  # [1, 2, 3, 4, 5](添加每个元素)

关键代码解释

方法效果结果
append([4,5])添加整个列表[1, 2, 3, [4, 5]]
extend([4,5])添加每个元素[1, 2, 3, 4, 5]

删除元素

删除方法: 从列表中移除元素。

删除方法对比:
┌─────────────────────────────────────────────────────────────┐
│  删除元素方法                                                 │
│                                                              │
│  方法        按什么删除    返回值    不存在时                │
│  ─────────────────────────────────────────────              │
│  remove()    值           无       ValueError               │
│  pop()       索引         元素     IndexError              │
│  del         索引/切片    无       IndexError              │
│  clear()     全部         无       -                        │
│                                                              │
│  选择建议:                                                   │
│  • 知道值 → remove()                                         │
│  • 知道位置 → pop()                                           │
│  • 删除范围 → del                                             │
│  • 清空列表 → clear()                                         │
└─────────────────────────────────────────────────────────────┘

最简示例

python
fruits = ["apple", "banana", "orange"]

# 按值删除
fruits.remove("banana")
print(fruits)  # ['apple', 'orange']

关键代码解释

方法删除方式返回值
remove("x")按值删除
pop(i)按索引删除返回被删元素
del list[i]按索引删除

详细示例

python
fruits = ["apple", "banana", "orange", "grape", "mango"]

# pop() - 按索引删除并返回元素
last = fruits.pop()       # 删除最后一个,返回 'mango'
first = fruits.pop(0)     # 删除第一个,返回 'apple'
print(fruits)  # ['banana', 'orange', 'grape']

# del 语句 - 按索引删除(不返回)
del fruits[0]             # 删除第一个
del fruits[1:3]           # 删除切片范围

# clear() - 清空列表
fruits.clear()
print(fruits)  # []

查找和统计

python
fruits = ["apple", "banana", "orange", "banana", "grape"]

# index() - 查找元素索引
print(fruits.index("banana"))      # 1(第一个匹配)
print(fruits.index("banana", 2))   # 3(从索引 2 开始查找)

# count() - 统计元素出现次数
print(fruits.count("banana"))      # 2
print(fruits.count("mango"))       # 0

# len() - 获取长度
print(len(fruits))                 # 5

# in 运算符 - 成员检查
print("apple" in fruits)           # True
print("mango" in fruits)           # False

# 查找所有匹配索引
indices = [i for i, x in enumerate(fruits) if x == "banana"]
print(indices)  # [1, 3]

排序和反转

python
numbers = [3, 1, 4, 1, 5, 9, 2, 6]

# sort() - 原地排序(修改原列表)
numbers.sort()                 # 升序
print(numbers)  # [1, 1, 2, 3, 4, 5, 6, 9]

numbers.sort(reverse=True)     # 降序
print(numbers)  # [9, 6, 5, 4, 3, 2, 1, 1]

# sorted() - 返回新列表(不修改原列表)
original = [3, 1, 4, 1, 5]
sorted_copy = sorted(original)
print(original)     # [3, 1, 4, 1, 5](不变)
print(sorted_copy)  # [1, 1, 3, 4, 5]

# reverse() - 原地反转
numbers.reverse()
print(numbers)

# reversed() - 返回迭代器
reversed_iter = reversed(numbers)
reversed_list = list(reversed(numbers))

# 按条件排序
words = ["banana", "pie", "apple", "cherry"]
words.sort(key=len)                    # 按长度排序
words.sort(key=str.lower)              # 按小写排序
words.sort(key=lambda x: x[-1])        # 按最后一个字母排序

# 多条件排序
students = [("Alice", 85), ("Bob", 90), ("Charlie", 85)]
students.sort(key=lambda x: (-x[1], x[0]))  # 先按分数降序,再按名字升序

其他方法

python
# copy() - 浅拷贝
original = [1, 2, 3]
copy_list = original.copy()

# 列表比较
list1 = [1, 2, 3]
list2 = [1, 2, 3]
print(list1 == list2)   # True(内容相同)
print(list1 is list2)   # False(不同对象)

# 列表连接和重复
list1 = [1, 2]
list2 = [3, 4]
print(list1 + list2)    # [1, 2, 3, 4]
print(list1 * 3)        # [1, 2, 1, 2, 1, 2]

# 最大值、最小值、求和
numbers = [1, 2, 3, 4, 5]
print(max(numbers))     # 5
print(min(numbers))     # 1
print(sum(numbers))     # 15
print(sum(numbers, 10)) # 25(初始值 10)

第三部分:列表复制

浅拷贝 vs 深拷贝

概念说明

列表复制是 Python 中容易混淆的概念,理解浅拷贝和深拷贝的区别很重要。

┌─────────────────────────────────────────────────────────────┐
│                  浅拷贝 vs 深拷贝                             │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   浅拷贝(Shallow Copy):                                   │
│   • 创建新列表对象                                           │
│   • 复制元素的引用(不复制元素本身)                         │
│   • 嵌套对象仍然是共享的                                     │
│                                                             │
│   深拷贝(Deep Copy):                                      │
│   • 创建新列表对象                                           │
│   • 递归复制所有嵌套对象                                     │
│   • 完全独立,不共享任何对象                                 │
│                                                             │
│   方法对比:                                                 │
│   ─────────────────────────────────────────────             │
│   赋值 (=)      不拷贝,只是引用                            │
│   copy()       浅拷贝                                       │
│   list[:]      浅拷贝                                       │
│   deepcopy()   深拷贝                                       │
│                                                             │
└─────────────────────────────────────────────────────────────┘

代码示例:

python
import copy

# 赋值(不拷贝)
original = [1, 2, 3]
reference = original
reference[0] = 100
print(original)   # [100, 2, 3](原列表也被修改)
print(reference)  # [100, 2, 3]

# 浅拷贝
original = [1, 2, 3]
shallow = original.copy()  # 或 original[:] 或 list(original)
shallow[0] = 100
print(original)  # [1, 2, 3](原列表不变)
print(shallow)   # [100, 2, 3]

# 浅拷贝的问题:嵌套列表
original = [[1, 2], [3, 4]]
shallow = original.copy()
shallow[0][0] = 100
print(original)   # [[100, 2], [3, 4]](嵌套列表被修改!)
print(shallow)    # [[100, 2], [3, 4]]

# 深拷贝:解决嵌套问题
original = [[1, 2], [3, 4]]
deep = copy.deepcopy(original)
deep[0][0] = 100
print(original)   # [[1, 2], [3, 4]](原列表不变)
print(deep)       # [[100, 2], [3, 4]]

图示:

┌─────────────────────────────────────────────────────────────┐
│                  拷贝示意图                                   │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   赋值(引用):                                             │
│   original ──────┐                                          │
│                   ───→ [1, 2, 3]                            │
│   reference ─────┘                                          │
│   (两个变量指向同一个对象)                                 │
│                                                             │
│   浅拷贝:                                                   │
│   original ────────→ [1, 2, 3]                              │
│   shallow  ────────→ [1, 2, 3](新对象)                    │
│   (两个独立对象,但元素是引用)                             │
│                                                             │
│   浅拷贝(嵌套列表):                                       │
│   original ───→ [[ref1], [ref2]]                            │
│   shallow  ───→ [[ref1], [ref2]]                            │
│                    ↓      ↓                                  │
│                  [1,2]  [3,4](共享嵌套对象)                │
│                                                             │
│   深拷贝:                                                   │
│   original ───→ [[1,2], [3,4]]                              │
│   deep     ───→ [[1,2], [3,4]](完全独立)                  │
│                                                             │
└─────────────────────────────────────────────────────────────┘

第四部分:列表作为栈和队列

列表作为栈

概念说明

栈(Stack)是后进先出(LIFO)的数据结构,Python 列表天然支持栈操作。

┌─────────────────────────────────────────────────────────────┐
│                    栈(Stack)                                │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   特点:后进先出(LIFO - Last In First Out)                 │
│                                                             │
│   操作:                                                     │
│   • push:入栈(添加元素)                                   │
│   • pop:出栈(移除并返回栈顶元素)                          │
│   • peek:查看栈顶元素                                       │
│                                                             │
│   Python 实现:                                              │
│   • push → append()                                         │
│   • pop → pop()                                             │
│   • peek → list[-1]                                         │
│                                                             │
│   示例:                                                     │
│   ┌───────────────────────────────────────────────┐         │
│   │  push(1)  →  [1]                              │         │
│   │  push(2)  →  [1, 2]                           │         │
│   │  push(3)  →  [1, 2, 3]                        │         │
│   │  pop()    →  返回 3,列表变为 [1, 2]          │         │
│   │  pop()    →  返回 2,列表变为 [1]             │         │
│   └───────────────────────────────────────────────┘         │
│                                                             │
└─────────────────────────────────────────────────────────────┘

代码示例:

python
# 使用列表作为栈
stack = []

# 入栈
stack.append(1)
stack.append(2)
stack.append(3)
print(stack)  # [1, 2, 3]

# 出栈
top = stack.pop()
print(top)    # 3
print(stack)  # [1, 2]

# 查看栈顶
if stack:
    print(stack[-1])  # 2

# 检查栈是否为空
print(len(stack) == 0)  # False
print(not stack)        # False

# 实际应用:括号匹配
def is_valid_parentheses(s: str) -> bool:
    stack = []
    pairs = {')': '(', ']': '[', '}': '{'}
    for char in s:
        if char in pairs.values():  # 左括号
            stack.append(char)
        elif char in pairs.keys():  # 右括号
            if not stack or stack.pop() != pairs[char]:
                return False
    return len(stack) == 0

print(is_valid_parentheses("()[]{}"))  # True
print(is_valid_parentheses("([)]"))    # False

列表作为队列

概念说明

队列(Queue)是先进先出(FIFO)的数据结构。列表作为队列效率不高,推荐使用 collections.deque

┌─────────────────────────────────────────────────────────────┐
│                    队列(Queue)                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   特点:先进先出(FIFO - First In First Out)                │
│                                                             │
│   操作:                                                     │
│   • enqueue:入队(添加元素到队尾)                          │
│   • dequeue:出队(移除并返回队首元素)                      │
│                                                             │
│   列表实现(不推荐):                                        │
│   • enqueue → append()                                      │
│   • dequeue → pop(0)  ← O(n) 效率低!                       │
│                                                             │
│   deque 实现(推荐):                                        │
│   • enqueue → append()                                      │
│   • dequeue → popleft()  ← O(1) 效率高!                    │
│                                                             │
│   示例:                                                     │
│   ┌───────────────────────────────────────────────┐         │
│   │  enqueue(1) →  [1]                            │         │
│   │  enqueue(2) →  [1, 2]                         │         │
│   │  enqueue(3) →  [1, 2, 3]                      │         │
│   │  dequeue()  →  返回 1,列表变为 [2, 3]        │         │
│   │  dequeue()  →  返回 2,列表变为 [3]           │         │
│   └───────────────────────────────────────────────┘         │
│                                                             │
└─────────────────────────────────────────────────────────────┘

代码示例:

python
from collections import deque

# 使用 deque 作为队列(推荐)
queue = deque()

# 入队
queue.append(1)
queue.append(2)
queue.append(3)
print(queue)  # deque([1, 2, 3])

# 出队
front = queue.popleft()
print(front)  # 1
print(queue)  # deque([2, 3])

# 从左侧入队
queue.appendleft(0)
print(queue)  # deque([0, 2, 3])

# 从右侧出队
right = queue.pop()
print(right)  # 3

# 列表作为队列(不推荐,效率低)
list_queue = [1, 2, 3]
list_queue.append(4)     # O(1)
list_queue.pop(0)        # O(n) ← 所有元素都要移动!

# deque 其他方法
d = deque([1, 2, 3])
d.extend([4, 5])         # 右侧扩展
d.extendleft([0])        # 左侧扩展(注意顺序反转)
d.rotate(1)              # 向右旋转 1 位:[5, 1, 2, 3, 4]
d.rotate(-1)             # 向左旋转 1 位
d.clear()                # 清空

第五部分:嵌套列表

二维列表(矩阵)

python
# 创建二维列表
matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

# 访问元素
print(matrix[0])      # [1, 2, 3](第一行)
print(matrix[0][1])   # 2(第一行第二列)
print(matrix[-1][-1]) # 9(最后一行最后一列)

# 创建矩阵的正确方式
# ❌ 错误方式(引用共享)
matrix = [[0] * 3] * 3
matrix[0][0] = 1
print(matrix)  # [[1, 0, 0], [1, 0, 0], [1, 0, 0]](所有行被修改!)

# ✅ 正确方式
matrix = [[0] * 3 for _ in range(3)]
matrix[0][0] = 1
print(matrix)  # [[1, 0, 0], [0, 0, 0], [0, 0, 0]]

# 遍历矩阵
for row in matrix:
    for element in row:
        print(element, end=' ')
    print()

# 使用 enumerate
for i, row in enumerate(matrix):
    for j, element in enumerate(row):
        print(f"matrix[{i}][{j}] = {element}")

# 矩阵转置
transposed = [[row[i] for row in matrix] for i in range(len(matrix[0]))]
# 或使用 zip
transposed = list(map(list, zip(*matrix)))

三维列表

python
# 创建三维列表
cube = [[[0 for _ in range(3)] for _ in range(3)] for _ in range(3)]

# 访问元素
cube[0][0][0] = 1  # 第一层第一行第一列

# 遍历三维列表
for i, layer in enumerate(cube):
    for j, row in enumerate(layer):
        for k, element in enumerate(row):
            print(f"cube[{i}][{j}][{k}] = {element}")

第六部分:列表性能

时间复杂度

┌─────────────────────────────────────────────────────────────┐
│                  列表操作时间复杂度                           │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   操作              时间复杂度    说明                       │
│   ─────────────────────────────────────────────             │
│   索引访问          O(1)         list[i]                    │
│   索引赋值          O(1)         list[i] = x                │
│   append()          O(1)         添加到末尾                  │
│   pop()             O(1)         从末尾删除                  │
│   pop(0)            O(n)         从开头删除                  │
│   insert(0, x)      O(n)         在开头插入                  │
│   remove(x)         O(n)         查找并删除                  │
│   in / index()      O(n)         查找元素                    │
│   切片              O(k)         k 是切片长度                │
│   len()             O(1)         获取长度                    │
│   sort()            O(n log n)   排序                        │
│   reverse()         O(n)         反转                        │
│                                                             │
│   性能建议:                                                 │
│   • 频繁在开头插入/删除 → 使用 deque                        │
│   • 频繁查找元素 → 使用 set 或 dict                         │
│   • 大量数据排序 → sort() 比 sorted() 快(原地)            │
│                                                             │
└─────────────────────────────────────────────────────────────┘

性能测试:

python
import time

# 测试 append vs insert
def test_append(n):
    lst = []
    for i in range(n):
        lst.append(i)
    return lst

def test_insert(n):
    lst = []
    for i in range(n):
        lst.insert(0, i)  # 在开头插入
    return lst

n = 100000
start = time.time()
test_append(n)
print(f"append: {time.time() - start:.4f}s")  # 约 0.01s

start = time.time()
test_insert(n)
print(f"insert: {time.time() - start:.4f}s")  # 约 5s(慢很多!)

# 测试查找:列表 vs 集合
import timeit

lst = list(range(100000))
s = set(range(100000))

# 列表查找 O(n)
print(timeit.timeit('99999 in lst', globals=globals(), number=1000))
# 约 0.5s

# 集合查找 O(1)
print(timeit.timeit('99999 in s', globals=globals(), number=1000))
# 约 0.0001s

L2 实践层:用好

最佳实践

列表方法选择指南

场景推荐方法原因
添加单个元素到末尾append()O(1),最高效
添加多个元素到末尾extend()一次添加,避免多次 append
指定位置插入insert()O(n),尽量少用
删除末尾元素pop()O(1),高效
删除指定值remove()O(n),需先查找
删除指定位置del list[i]不需要返回值时
清空列表clear()list = [] 更明确
复制列表copy()[:]浅拷贝,简单
排序sort() 原地 / sorted() 新列表根据是否保留原列表决定

反模式:不要这样做

python
# ❌ 反模式 1:遍历时删除元素
numbers = [1, 2, 3, 4, 5]
for n in numbers:
    if n % 2 == 0:
        numbers.remove(n)  # 漏删!索引变化

# ✅ 正确做法:列表推导式
numbers = [n for n in numbers if n % 2 != 0]

# 或遍历副本
for n in numbers[:]:
    if n % 2 == 0:
        numbers.remove(n)
python
# ❌ 反模式 2:用乘法创建嵌套列表
matrix = [[0] * 3] * 3  # 所有行共享引用!
matrix[0][0] = 1
print(matrix)  # [[1, 0, 0], [1, 0, 0], [1, 0, 0]]

# ✅ 正确做法:列表推导式
matrix = [[0] * 3 for _ in range(3)]  # 每行独立对象
matrix[0][0] = 1
print(matrix)  # [[1, 0, 0], [0, 0, 0], [0, 0, 0]]
python
# ❌ 反模式 3:可变默认参数
def add_item(item, lst=[]):  # 默认列表被共享!
    lst.append(item)
    return lst

add_item(1)  # [1]
add_item(2)  # [1, 2] ← 不是 [2]!

# ✅ 正确做法:用 None 作为默认值
def add_item(item: int, lst: list[int] | None = None) -> list[int]:
    if lst is None:
        lst = []
    lst.append(item)
    return lst
python
# ❌ 反模式 4:列表作为队列(开头操作)
queue = [1, 2, 3]
queue.pop(0)  # O(n),所有元素移动!

# ✅ 正确做法:使用 deque
from collections import deque
queue = deque([1, 2, 3])
queue.popleft()  # O(1)
python
# ❌ 反模式 5:频繁用 + 连接列表
result = []
for item in items:
    result = result + [item]  # 每次创建新列表,O(n²)

# ✅ 正确做法:用 extend 或 append
result = []
for item in items:
    result.append(item)  # O(n)

适用场景

场景是否推荐用列表原因
存储有序数据序列✅ 推荐列表天生有序
需要索引访问✅ 推荐O(1) 索引
频末尾添加/删除✅ 推荐append/pop O(1)
频开头添加/删除❌ 不推荐用 deque,O(1) vs O(n)
频查找元素是否存在❌ 不推荐用 set,O(1) vs O(n)
需要唯一元素❌ 不推荐用 set
键值对映射❌ 不推荐用 dict
不可变序列❌ 不推荐用 tuple

性能专项:append vs insert

python
import timeit

# append 在末尾添加 — O(1) 平均
print(timeit.timeit('[].append(1)', number=10_000_000))
# ~0.3s

# insert(0, x) 在开头插入 — O(n)
print(timeit.timeit('[].insert(0, 1)', number=10_000_000))
# ~2.5s(慢 8 倍!)

# 结论:频繁插入开头 → 使用 collections.deque

推导式 vs 手动循环

python
import timeit

# 列表推导式 — 字节码优化(LIST_APPEND 指令)
print(timeit.timeit('[x ** 2 for x in range(1000)]', number=10000))
# ~0.35s

# 手动循环 — append 方法调用开销
print(timeit.timeit('''
r = []
for x in range(1000):
    r.append(x ** 2)
''', number=10000))
# ~0.55s(慢 ~60%)

# 结论:推导式不仅简洁,而且更快

切片 vs copy() vs list()

python
import timeit

a = list(range(1000))

# 三者等价 — 都是浅拷贝
print(timeit.timeit('a[:]', globals={'a': a}, number=100000))       # 最快
print(timeit.timeit('a.copy()', globals={'a': a}, number=100000))   # 中等
print(timeit.timeit('list(a)', globals={'a': a}, number=100000))     # 最慢

# 推荐:a[:] 最快,a.copy() 可读性最好(显式表明意图)

L3 专家层:深入

底层原理:动态数组

Python 如何实现列表

列表在 Python 中使用动态数组实现:

列表内部结构:
┌─────────────────────────────────────────────────────────────┐
│                    动态数组原理                                │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   内存布局:                                                 │
│   ┌────────────────────────────────────────────┐           │
│   │ [0]  [1]  [2]  [3]  [4]  [空] [空] [空]    │           │
│   │  元素区(已用)      预留区(未用)          │           │
│   └────────────────────────────────────────────┘           │
│                                                             │
│   关键参数:                                                 │
│   • len:元素数量(5)                                       │
│   • capacity:总容量(8)                                    │
│   • growth factor:增长因子(约 1.125 倍)                   │
│                                                             │
│   扩容策略:                                                 │
│   • 空列表:capacity = 0                                     │
│   • 添加元素时容量不足 → 申请更大内存                        │
│   • 复制所有元素到新内存 → 释放旧内存                        │
│   • 新容量 ≈ 旧容量 × 1.125(Python 3.11+)                  │
│                                                             │
└─────────────────────────────────────────────────────────────┘

演示扩容过程

python
import sys

lst = []
for i in range(10):
    lst.append(i)
    print(f"len={len(lst)}, size={sys.getsizeof(lst)} bytes")
    
# 输出:
# len=0,  size=56 bytes   (空列表)
# len=1,  size=64 bytes   (扩容到 4)
# len=2,  size=64 bytes
# len=3,  size=64 bytes
# len=4,  size=64 bytes
# len=5,  size=72 bytes   (扩容到 8)
# len=6,  size=72 bytes
# len=7,  size=72 bytes
# len=8,  size=72 bytes
# len=9,  size=80 bytes   (再次扩容)

性能考量

操作时间复杂度原因
list[i] 索引访问O(1)连续内存,直接计算地址
list[i] = x 索引赋值O(1)直接写入内存位置
append()O(1) 平均末尾添加,偶尔扩容 O(n)
pop()O(1)末尾删除
insert(0, x)O(n)所有元素后移
pop(0)O(n)所有元素前移
remove(x)O(n)查找 + 移动
x in listO(n)线性查找
len()O(1)存储在对象头部

设计动机

设计选择原因替代方案对比
动态数组索引 O(1),内存紧凑链表:索引 O(n),内存分散
预留容量避免频繁扩容无预留:每次添加都申请内存
增长因子 ~1.125平衡内存浪费和扩容次数2 倍:内存浪费多,扩容少

知识关联

列表知识关联图:
                    ┌─────────────────┐
                    │    序列协议     │
                    │  __getitem__    │
                    │  __len__        │
                    └─────────────────┘

           ┌───────────────┼───────────────┐
           │               │               │
           ▼               ▼               ▼
    ┌───────────┐   ┌───────────┐   ┌───────────┐
    │   列表    │   │   元组    │   │   字符串  │
    │  list     │   │  tuple    │   │   str     │
    │ 可变有序  │   │ 不可变    │   │ 不可变    │
    └───────────┘   └───────────┘   └───────────┘

           │ 需要快速开头操作

    ┌───────────┐
    │   deque   │
    │ 双端队列  │
    │ O(1)两端  │
    └───────────┘

           │ 需要唯一性/快速查找

    ┌───────────┐
    │   集合    │
    │   set     │
    │ 唯一元素  │
    │ O(1)查找  │
    └───────────┘

字节码验证:推导式 vs 循环

python
import dis

# 列表推导式 — 使用 LIST_APPEND 专用指令
dis.dis("[x ** 2 for x in range(5)]")
# 输出关键指令:
#   BUILD_LIST        创建空列表
#   GET_ITER          获取迭代器
#   LIST_APPEND  2    追加元素 ← 专用字节码,O(1) 无方法查找

# 手动循环 — 使用 CALL_METHOD 调用 append
def manual_loop():
    r = []
    for x in range(5):
        r.append(x ** 2)
    return r

dis.dis(manual_loop)
# 输出关键指令:
#   LOAD_METHOD   append  ← 方法查找开销
#   CALL_METHOD   1       ← 函数调用开销
# 结论:LIST_APPEND 比 LOAD_METHOD + CALL_METHOD 快 ~60%

扩容策略验证

python
import sys

lst = []
prev = sys.getsizeof(lst)
for i in range(20):
    lst.append(i)
    cur = sys.getsizeof(lst)
    if cur != prev:
        print(f"len={len(lst):2d}  size={cur:4d} bytes  扩容!")
        prev = cur

# 典型输出(Python 3.11+):
# len= 1  size=  88 bytes  扩容!  (容量 4)
# len= 5  size= 120 bytes  扩容!  (容量 8)
# len= 9  size= 184 bytes  扩容!  (容量 16)
# 增长模式:0→4→8→16→24→32→...

# 预分配优化:提前知道大小时
n = 1_000_000
pre_alloc = [None] * n  # 一次性分配
for i in range(n):
    pre_alloc[i] = i
# 避免 20+ 次扩容,减少内存拷贝

第九部分:常见错误

常见陷阱

python
# 陷阱 1:修改列表时遍历
numbers = [1, 2, 3, 4, 5]
# ❌ 错误:遍历时删除
for n in numbers:
    if n % 2 == 0:
        numbers.remove(n)
print(numbers)  # [1, 3, 5](可能漏删)

# ✅ 正确:使用切片复制或列表推导
numbers = [n for n in numbers if n % 2 != 0]
# 或
for n in numbers[:]:  # 遍历副本
    if n % 2 == 0:
        numbers.remove(n)

# 陷阱 2:列表乘法创建嵌套列表
matrix = [[0] * 3] * 3  # ❌ 所有行共享引用
matrix[0][0] = 1
print(matrix)  # [[1, 0, 0], [1, 0, 0], [1, 0, 0]]

# ✅ 正确
matrix = [[0] * 3 for _ in range(3)]

# 陷阱 3:默认参数使用列表
def add_item(item, lst=[]):  # ❌ 默认列表会被修改
    lst.append(item)
    return lst

print(add_item(1))  # [1]
print(add_item(2))  # [1, 2](不是 [2]!)

# ✅ 正确
def add_item(item, lst=None):
    if lst is None:
        lst = []
    lst.append(item)
    return lst

# 陷阱 4:整数列表的乘法
a = [1, 2, 3]
b = a * 2  # [1, 2, 3, 1, 2, 3](复制元素)
# 不是 [2, 4, 6](数学乘法)

# 陷阱 5:比较列表
[1, 2] == [1, 2]   # True(内容相同)
[1, 2] is [1, 2]   # False(不同对象)

本章小结

┌─────────────────────────────────────────────────────────────┐
│                      列表 知识要点                           │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   创建:                                                     │
│   ✓ [1, 2, 3]、list()、推导式                               │
│                                                             │
│   访问:                                                     │
│   ✓ 索引 [0]、切片 [start:end:step]                         │
│   ✓ 正向索引(0 开始)、反向索引(-1 开始)                  │
│                                                             │
│   方法:                                                     │
│   ✓ 添加:append、insert、extend                            │
│   ✓ 删除:remove、pop、del、clear                           │
│   ✓ 查找:index、count、in                                  │
│   ✓ 排序:sort(原地)、sorted(新列表)                    │
│                                                             │
│   复制:                                                     │
│   ✓ 赋值 (=):引用,不拷贝                                  │
│   ✓ 浅拷贝:copy()、[:]、list()                             │
│   ✓ 深拷贝:copy.deepcopy()                                 │
│                                                             │
│   特殊用途:                                                 │
│   ✓ 栈:append + pop(LIFO)                                │
│   ✓ 队列:使用 deque(FIFO)                                │
│   ✓ 矩阵:嵌套列表                                          │
│                                                             │
│   性能:                                                     │
│   ✓ 索引 O(1)、末尾操作 O(1)                                │
│   ✓ 开头操作 O(n)、查找 O(n)                                │
│                                                             │
└─────────────────────────────────────────────────────────────┘