切片(slicing)是 Python 里最”看起来简单,实际藏了很多细节”的语法。前面 列表、元组、字符串 都用过 a[1:3],但你可能不知道:切片有第三个参数(步长)、切片赋值可以改变长度、slice 对象是可以复用的、numpy 数组切片是视图而不是拷贝……这一篇要把切片的”心智模型”讲通,让你以后遇到任何切片问题都能自己推导出来。
一、切片的三段式
完整语法:
1 | 序列[start:stop:step] |
- start:起始索引(含),默认为
0(step > 0)或-1(step < 0); - stop:结束索引(不含),默认为
len(seq)或-len(seq)-1; - step:步长,默认为
1。
三个参数都可以省略,冒号不能省。
1 | a = [10, 20, 30, 40, 50, 60, 70] |
二、切片的”半开区间”
Python 切片遵循数学的”半开区间” [start, stop):含 start,不含 stop。
好处:
len(a[i:j]) == j - i(当区间合法时),很好算;- 拼接方便:
a[:i] + a[i:]就是原列表; a[i:i]就是空切片,长度 0。
三、负数索引与负步长
1 | a = [10, 20, 30, 40, 50] |
规则:负数索引 = 长度 + 索引。a[-1] 就是 a[len(a)-1]。
负步长时:默认 start 是最后一个元素,stop 是”越过第一个的位置”。
四、越界不报错
切片的一个奇妙特性是从不越界报错:
1 | a = [1, 2, 3] |
Python 会自动把越界的索引”夹紧”到合法范围。
五、切片赋值
列表(可变序列)支持切片赋值,这是切片最强大的用法:
1 | a = [1, 2, 3, 4, 5] |
注意:字符串和元组是不可变的,不支持切片赋值。
六、切片是浅拷贝
b = a[:] 是列表的浅拷贝:
1 | a = [[1, 2], [3, 4]] |
要独立的深拷贝:copy.deepcopy(a)。
七、slice 对象
[start:stop:step] 本质是 slice(start, stop, step) 对象:
1 | s = slice(1, 5, 2) |
好处:
- 复用切片规则:
s = slice(2, None)起名HEAD_TWO_SKIP,在多个数组里复用; - 传参:函数接收
slice参数比接收start/stop/step三个参数干净; - NumPy 里切片对象大量使用。
八、字符串切片
字符串切片行为跟列表一致,但返回新字符串(因为不可变):
1 | s = "abcdefg" |
九、字节和 bytearray
1 | b = b"hello" |
bytes 是不可变的(像 str),bytearray 是可变的(像 list of bytes)。
十、多维切片(NumPy 预告)
Python 内置容器只支持一维切片。多维切片是 NumPy 的强项:
1 | import numpy as np |
注意:NumPy 切片返回视图(view)而不是拷贝,修改视图会修改原数组。这跟内置的 list/str/tuple 相反。
十一、切片的性能特性
- 内置容器切片是 O(k),k 是切片长度(要复制数据);
- 切片会分配新对象,频繁切片会增加内存压力;
- 单纯”跳过前 n 个”若数据量大,考虑用
itertools.islice:
1 | from itertools import islice |
islice 支持任何可迭代对象且懒惰,比先 list() 再切片省内存。
十二、切片常用套路
1. 反转
1 | s[::-1] # 字符串/列表反转 |
2. 拷贝
1 | b = a[:] |
3. 分块
1 | def chunks(lst, n): |
4. 弹出多个
1 | head, *rest = a # 用解包 |
5. 移除头/尾
1 | a = a[1:] # 去掉第一个 |
6. 保留最后 n 个
1 | last_n = a[-n:] |
7. 交换两段
1 | mid = len(a) // 2 |
十三、切片与索引的边界
1 | a = [1, 2, 3, 4, 5] |
初学者常常混淆索引和切片的边界,简单的记忆:
- 单索引:含,越界报错;
- 切片:start 含,stop 不含,越界不报错。
十四、一个综合例子:滚动窗口
1 | # rolling.py |
十五、常见陷阱
陷阱 1:s[::-1] 反转对 dict/set 无效
只有序列(list/tuple/str/bytes/range 等)支持切片。dict 和 set 是无序的,不能切片。
1 | {1, 2, 3}[::-1] # ❌ TypeError |
陷阱 2:切片赋值时步长冲突
1 | a = [1, 2, 3, 4] |
陷阱 3:a[:] = b 与 a = b 不同
1 | a = [1, 2, 3] |
a[:] = b 是”就地替换 a 的全部元素”,a = b 是”让 a 名字指向新对象”。
陷阱 4:字符串切片”高开销”
Python 字符串切片会复制内存。逐字符处理超大字符串时,切片可能比想象中慢。
陷阱 5:NumPy 切片是视图,Python list 是拷贝
1 | lst = [1, 2, 3] |
十六、小结与延伸阅读
- 切片语法
[start:stop:step],含头不含尾; - 越界不报错,索引越界才报错;
- 负数索引 = 长度 + 索引;
- 切片是浅拷贝,嵌套对象共享;
- 列表支持切片赋值,可变增/删;
slice(start, stop, step)是可复用的切片对象;- 反转
s[::-1]、拷贝s[:]、分块用切片; - 大数据流用
itertools.islice; - NumPy 切片是视图,行为不同。
延伸阅读:
- 官方序列文档:https://docs.python.org/zh-cn/3/library/stdtypes.html#sequence-types-list-tuple-range
- itertools.islice:https://docs.python.org/zh-cn/3/library/itertools.html#itertools.islice
下一篇 常用内置函数汇总 我们把 Python 内置的 70+ 个函数一次梳理清楚。