第 2 章 · 序列
这一章在讲什么
Section titled “这一章在讲什么”一句话:Python 的序列类型有一张共同的接口,认熟它,就不用每种类型重新学一遍。
字符串、列表、元组、字节串、array、XML 元素、数据库查询结果,都共享一套操作: 迭代、切片、排序、拼接、解包。你在 list 上学到的东西,大部分能直接搬到别的序列上。
两种分法,四条轴
Section titled “两种分法,四条轴”分法一:能改 vs 不能改。
| 例子 | |
|---|---|
| 可变序列 | list、bytearray、array.array、collections.deque |
| 不可变序列 | tuple、str、bytes |
分法二:装引用 vs 存值。
| 装什么 | 例子 | 特点 | |
|---|---|---|---|
| 容器序列(container) | 一堆指向对象的引用 | list、tuple、deque |
灵活,能嵌套任意类型 |
| 扁平序列(flat) | 自己的内存里直接放机器值 | str、bytes、array.array |
紧凑、快,但只能放数字/字符/字节 |
这个区分不是术语洁癖,它直接决定内存占用。我在本机量了一下,同样 1000 个浮点数:
list 对象(头 + 指针数组) 8056 字节list 里 1000 个 float 对象 24000 字节list 总占用 32056 字节array 总占用 8080 字节array 省下的约 4 倍内存,全部来自「不给每个数单独造一个对象」。内存里的一个 float
对象是 ob_refcnt + ob_type + ob_fval 三个字段,64 位下每个 8 字节;list 里
每个元素还要再存一个 8 字节指针。
这一章分成哪几页
Section titled “这一章分成哪几页”| 页 | 讲什么 | 我的结论 |
|---|---|---|
| 推导式与生成器表达式 | listcomp 和 genexp | 造列表用 listcomp,喂别的构造器用 genexp |
| 元组的两副面孔 | 记录 vs 不可变列表 | 「不可变」只管引用,管不到引用指向的对象 |
| 解包 | * 的各种用法 |
不用下标取值,就不用写错下标 |
| 序列模式匹配 | match/case |
不是 switch,是解构加类型检查 |
| 切片 | 切片对象、切片赋值 | 切片可以取名字,左边也能放切片 |
| + 和 * 和 += | 拼接与原地修改 | [[]] * 3 是个陷阱,+= 对元组是造新对象 |
| 排序 | sort / sorted / key |
稳定排序,key 只调一次 |
| 列表之外 | array / memoryview / deque | 数字多就换 array,两端增删就换 deque |
uv run pytest tests/test_ch02.py -vcd chapters/ch02 && uv run python -c "import pitfalls; print(pitfalls.augmented_assignment_puzzler())"chapters/ch02/ 下七个模块,对应上面七页里的可运行部分。