Python 切片语法中容易踩的坑
Python 切片语法中容易踩的坑
切片(slicing)是 Python 里最常用也最容易"看起来懂了,实际用错"的语法之一。本文梳理切片机制本身的规则,以及实践中最容易踩的坑。
1. 基础语法与默认值
a[start:stop:step] 中三个参数都可省略:
a = [0, 1, 2, 3, 4, 5]
a[2:4] # [2, 3]
a[:3] # [0, 1, 2],start 默认为 0
a[3:] # [3, 4, 5],stop 默认为序列末尾
a[:] # 整个序列的拷贝注意 stop 是不包含的(左闭右开),这是切片里最基础但最容易导致差一位错误(off-by-one)的地方。
2. 切片不会越界报错
这是切片和普通索引最大的区别:普通索引越界会抛 IndexError,但切片会自动截断到合法范围,不报错:
a = [0, 1, 2]
a[10] # IndexError
a[1:100] # [1, 2],不会报错,自动截断
a[100:200] # [],越界范围直接返回空列表这个"宽容"特性方便但也容易隐藏 bug:本以为切片越界会报错从而提醒你检查范围,结果它默默返回空结果。
3. 负数索引与负数步长
负数索引表示从末尾数起,-1 是最后一个元素:
a[-1] # 5,最后一个元素
a[-3:] # [3, 4, 5],最后三个
a[:-2] # [0, 1, 2, 3],去掉最后两个当 step 为负数时,start 和 stop 的默认值会反转——start 默认是序列末尾,stop 默认是序列开头之前(而不是 0):
a[::-1] # [5, 4, 3, 2, 1, 0],整体反转
a[4:1:-1] # [4, 3, 2],从索引4倒着取到索引1(不含1)容易踩的坑是:当 step 为负数时,如果还写成正向的 start < stop(比如 a[1:4:-1]),结果会是空列表,因为方向和范围矛盾。
4. 切片返回的是新对象,不是视图
对原生 list、tuple、str 做切片,得到的是一份新对象(浅拷贝),修改切片结果不会影响原序列:
a = [1, 2, 3]
b = a[:]
b.append(4)
print(a) # [1, 2, 3],a 没有变化但要注意"浅"拷贝的含义:如果元素本身是可变对象(比如嵌套列表),内层对象仍然是共享引用:
a = [[1, 2], [3, 4]]
b = a[:]
b[0].append(99)
print(a) # [[1, 2, 99], [3, 4]],内层列表被一起改了如果需要完全独立的拷贝,应使用 copy.deepcopy()。
注意这一点和 NumPy 数组不同:对 numpy.ndarray 切片得到的是视图,修改切片会直接改动原数组,这是从 list 转到 numpy 时最容易踩的坑之一。
5. 字符串/元组是不可变的,切片赋值会报错
切片赋值(替换内容)只对可变序列(如 list)有效,对 str、tuple 这类不可变类型切片赋值会直接报错:
s = "hello"
s[1:3] = "EL" # TypeError,字符串不支持切片赋值6. 列表切片赋值可以改变长度
这是切片赋值最特殊也最容易被忽视的一点:右侧序列的长度不需要和切片范围一致,赋值后列表长度会跟着变化:
a = [0, 1, 2, 3, 4]
a[1:3] = [9, 9, 9, 9] # 用4个元素替换原来2个元素
print(a) # [0, 9, 9, 9, 9, 3, 4],长度变长了
a[1:5] = [] # 用空列表替换,等价于删除这段
print(a) # [0, 4]也可以用 del a[start:stop] 直接删除一段元素,效果和上面的空列表赋值相同。
7. 切片赋值要求右侧是可迭代对象
a = [1, 2, 3]
a[1:2] = 5 # TypeError,必须是可迭代对象(如 [5])
a[1:2] = [5] # 正确新手常见错误是直接赋一个标量值,而不是包在列表里。
8. step 为 0 会报错
a[::0] # ValueError: slice step cannot be zero9. slice() 对象与 indices() 方法
切片语法 a[start:stop:step] 背后实际是构造了一个 slice 对象,可以显式创建并复用:
s = slice(1, 4, 2)
a[s] # 等价于 a[1:4:2]slice.indices(length) 可以把负数、越界、省略值统一规范化成实际的 (start, stop, step) 三元组,自己实现支持切片的类时很有用。
小结清单
- 切片左闭右开,越界不报错,会自动截断。
step为负时,start/stop的默认方向也会反转,注意方向与范围是否匹配。- 原生序列切片是浅拷贝,嵌套可变对象仍共享引用;NumPy 数组切片则是视图,行为不同。
- 不可变类型(str、tuple)不支持切片赋值。
- 列表切片赋值的右侧长度可以和切片范围不同,因此可以用来插入、替换、删除元素。
- 切片赋值右侧必须是可迭代对象,不能是单个标量。
step不能为 0。- 复杂场景可以用
slice()对象和indices()方法显式处理切片逻辑。