列表(list)是 Python 里使用最频繁的容器类型。要说 Python 的”灵魂容器”,字典可能占一半,列表就占另一半。学完基础后,几乎每一个项目、每一段脚本,你都会用到列表。这一篇要把列表讲通透:怎么创建、怎么增删改查、切片、常用方法、性能特性、以及一些新手才踩得到的坑。
一、什么是列表
Python 的列表是一个 可变、有序、可存放任意类型元素的容器。用中括号创建:
1 2 3
| empty = [] nums = [1, 2, 3, 4, 5] mixed = [1, "hello", 3.14, True, None, [1, 2]]
|
跟 C 的数组、Java 的 ArrayList 类似,但 Python 的列表不要求元素类型一致。底层是一段连续的指针数组,指向真实对象。
二、创建列表的多种方式
1 2 3 4 5 6 7 8 9 10 11 12 13
| a = [1, 2, 3]
b = list("abc") c = list(range(5)) d = list((1, 2, 3))
e = [x * 2 for x in range(5)]
zeros = [0] * 5
|
陷阱:[[]] * 3 得到的是三个指向同一个内层列表的引用!
1 2 3 4 5 6
| grid = [[]] * 3 grid[0].append(1) print(grid)
grid = [[] for _ in range(3)]
|
三、索引与切片
列表支持索引(正/负)和切片:
1 2 3 4 5 6 7 8 9
| a = [10, 20, 30, 40, 50]
print(a[0]) print(a[-1]) print(a[1:4]) print(a[:3]) print(a[2:]) print(a[::-1]) print(a[::2])
|
索引越界抛 IndexError:a[10] 会崩,但 a[10:20] 只会返回空列表。切片比索引更”宽容”。
切片返回新列表——修改切片不影响原列表:
1 2 3 4
| b = a[1:3] b[0] = 999 print(a) print(b)
|
想拷贝整个列表:b = a[:] 或 b = list(a) 或 b = a.copy()。
四、修改列表
列表可变,可以直接改:
1 2 3 4 5 6 7 8 9
| a = [1, 2, 3] a[0] = 99 print(a)
a[1:] = [7, 8, 9] print(a)
a[1:3] = [] print(a)
|
切片赋值非常强大,可以在中间插入元素:
1 2 3
| a = [1, 2, 3] a[1:1] = [10, 20] print(a)
|
五、增删改查方法
添加
1 2 3 4 5 6 7 8
| a = [1, 2, 3] a.append(4) a.extend([5, 6]) a.insert(0, 0)
a + [7] a += [7]
|
性能提示:append 是 O(1),insert(0, x) 是 O(n)(要移动所有元素)。频繁头部插入用 collections.deque。
删除
1 2 3 4 5 6 7 8 9 10
| a = [10, 20, 30, 20, 40]
a.remove(20) popped = a.pop() popped = a.pop(0)
del a[0] del a[:]
a.clear()
|
陷阱:remove 只删第一个匹配的,全部删除要用循环或列表推导式。
查找
1 2 3 4 5 6
| a = [10, 20, 30, 20, 40]
print(20 in a) print(a.index(20)) print(a.index(20, 2)) print(a.count(20))
|
找不到会抛 ValueError,所以查找前先 in 检查,或者用 try:
1 2 3 4
| try: i = a.index(99) except ValueError: i = -1
|
修改
除了索引赋值,还有排序和反转:
1 2 3 4 5 6 7 8 9
| a = [3, 1, 4, 1, 5, 9, 2, 6]
a.sort() a.sort(reverse=True) a.reverse()
b = sorted(a) c = list(reversed(a))
|
复杂排序
sort 和 sorted 都支持 key 参数:
1 2 3 4 5
| words = ["Banana", "apple", "cherry"] words.sort(key=str.lower)
people = [("张三", 25), ("李四", 20), ("王五", 30)] people.sort(key=lambda p: p[1])
|
多级排序:
1
| records.sort(key=lambda r: (r.grade, -r.score))
|
六、遍历
前面 循环语句 for 与 while 已讲过:
1 2 3 4 5 6 7 8
| for x in a: print(x)
for i, x in enumerate(a): print(i, x)
for x, y in zip(a, b): print(x, y)
|
七、常用统计与聚合
1 2 3 4 5 6 7
| a = [3, 1, 4, 1, 5, 9, 2, 6]
print(len(a)) print(min(a)) print(max(a)) print(sum(a)) print(sum(a) / len(a))
|
想要中位数、方差、标准差:statistics 模块。
1 2 3 4
| import statistics statistics.median(a) statistics.mean(a) statistics.stdev(a)
|
八、拷贝:浅拷贝 vs 深拷贝
前面提到 a[:] 拷贝,这是浅拷贝——只复制最外层,内层对象仍是共享的:
1 2 3 4 5 6 7 8 9 10 11
| import copy
original = [[1, 2], [3, 4]]
shallow = original[:] shallow[0][0] = 999 print(original)
deep = copy.deepcopy(original) deep[0][0] = 111 print(original)
|
规则:
- 元素都是不可变(int/str/tuple)时,浅拷贝够用;
- 元素是可变对象(list/dict/自定义类)且需要独立修改,用
copy.deepcopy。
九、列表作为栈和队列
栈(LIFO)用 list 完美:
1 2 3 4
| stack = [] stack.append(1) stack.append(2) stack.pop()
|
队列(FIFO)用 list 也行,但 pop(0) 是 O(n)。频繁头部弹出请用 collections.deque:
1 2 3 4 5 6
| from collections import deque q = deque([1, 2, 3]) q.append(4) q.appendleft(0) q.popleft() q.pop()
|
十、二维列表(矩阵)
1 2 3 4 5 6 7 8 9 10 11
| matrix = [[0] * 4 for _ in range(3)]
matrix[1][2] = 9
for row in matrix: for cell in row: print(cell, end=" ") print()
|
别用 [[0]*4] * 3——那是三个指向同一行的引用!这是新手最常翻车的地方之一。
十一、性能特性
- 索引访问:O(1)
append / pop(尾部):均摊 O(1)
insert(0, x) / pop(0):O(n)
x in list:O(n)(用 set 更快,见 集合 Set)
list.remove(x):O(n)
- 排序:O(n log n),用的是 Timsort,稳定排序
- 内存:一个元素约 8 字节指针 + 对象自身
十二、一个综合例子:图书管理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| """极简图书管理,演示列表增删改查、排序、查找。"""
books = []
def add(title: str, author: str, year: int) -> None: books.append({"title": title, "author": author, "year": year})
def remove(title: str) -> bool: for b in books: if b["title"] == title: books.remove(b) return True return False
def find_by_author(author: str) -> list[dict]: return [b for b in books if b["author"] == author]
def sort_by_year(desc: bool = False) -> list[dict]: return sorted(books, key=lambda b: b["year"], reverse=desc)
def top_n_recent(n: int) -> list[dict]: return sort_by_year(desc=True)[:n]
if __name__ == "__main__": add("Python 编程", "Guido", 2020) add("Fluent Python", "Ramalho", 2022) add("Effective Python", "Slatkin", 2019) add("Python Tricks", "Bader", 2018)
print("=== 全部(按年份升序)===") for b in sort_by_year(): print(b)
print("=== 最新两本 ===") for b in top_n_recent(2): print(b)
print("=== Guido 的书 ===") for b in find_by_author("Guido"): print(b)
remove("Python Tricks") print(f"删除后剩 {len(books)} 本")
|
十三、常见陷阱
陷阱 1:[[0]*n] * m 二维列表
1 2 3 4
| grid = [[0] * 3] * 2 grid[0][0] = 1 print(grid)
|
陷阱 2:循环中修改列表
1 2 3
| for x in items: if not valid(x): items.remove(x)
|
用推导式重建:items = [x for x in items if valid(x)]。
陷阱 3:默认参数是列表
1 2 3
| def add(item, lst=[]): lst.append(item) return lst
|
多次调用会共享同一个列表。改成:
1 2 3 4 5
| def add(item, lst=None): if lst is None: lst = [] lst.append(item) return lst
|
陷阱 4:== 与 is
1 2
| [1, 2] == [1, 2] [1, 2] is [1, 2]
|
判断值相等永远用 ==。
陷阱 5:+= 与 + 的区别
1 2 3 4 5 6 7 8 9
| a = [1, 2] b = a a += [3] print(b)
a = [1, 2] b = a a = a + [3] print(b)
|
十四、小结与延伸阅读
- 列表是可变、有序、任意类型的容器;
- 增:
append/extend/insert;删:remove/pop/del;改:索引/切片赋值;
- 排序有
list.sort(就地)和 sorted(返回新),都支持 key;
- 拷贝分浅/深,可变元素警惕引用共享;
- 头部操作慢,用 deque;
- 二维列表用
[[0]*n for _ in range(m)];
- 遍历时不要改容器。
延伸阅读:
下一篇 元组 Tuple 与不可变序列 我们讲列表的孪生兄弟——元组。