写 Python 的时候,你几乎每天都要从列表或字符串里"取一段"。这项工作靠的就是切片(slicing)。语法看起来简单,但细节不少,今天一次讲清楚。
切片的基本写法
切片的完整格式是 seq[start:end],含义是:从下标 start 开始取,取到下标 end 之前为止,不包含 end。
nums = [10, 20, 30, 40, 50]
print(nums[1:3]) # [20, 30],取下标 1、2,不含 3
print(nums[0:2]) # [10, 20]
print(nums[2:2]) # [],start 和 end 相同,取不到东西
记住一个口诀:包头不包尾。nums[1:3] 拿到的是下标 1 和 2 的元素,下标 3 那个永远取不到。这样设计有个好处:nums[1:3] 和 nums[3:5] 拼起来正好是 nums[1:5],区间首尾相接不重叠。
越界也不报错
切片另一个贴心的地方:下标越界不会抛异常,能取多少取多少。
nums = [10, 20, 30]
print(nums[1:100]) # [20, 30],end 超了就取到结尾
print(nums[5:10]) # [],完全超出就是空列表
对比一下,直接用 nums[5] 取下标会立刻抛 IndexError。所以批量取值时切片更"耐用",不用担心边界。
省略参数的常见写法
start 和 end 都可以省略:省略 start 表示从头开始,省略 end 表示取到结尾。
nums = [10, 20, 30, 40, 50]
print(nums[:3]) # [10, 20, 30],从头取到下标 3 之前
print(nums[2:]) # [30, 40, 50],从下标 2 取到结尾
print(nums[:]) # 整个列表,相当于浅拷贝一份
nums[:] 在实战里常用来快速复制列表:改拷贝不影响原列表。做数据加工时先拷贝一份再动手,能少踩不少坑。
step:隔几个取一个
切片还有第三个参数 step,完整格式是 seq[start:end:step]。
nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(nums[::2]) # [0, 2, 4, 6, 8],每隔一个取一个
print(nums[1::2]) # [1, 3, 5, 7, 9],从下标 1 开始隔一个取
print(nums[::-1]) # [9, 8, 7, ..., 0],step 为 -1,整个序列反转
[::-1] 是 Python 圈里最经典的写法之一,一行完成序列反转,值得背下来。
step 为负数时从后往前取,这时 start 要写在 end 的右边才有结果:
nums = [0, 1, 2, 3, 4, 5]
print(nums[5:1:-1]) # [5, 4, 3, 2],从下标 5 往前取到下标 2
print(nums[1:5:-1]) # [],方向对不上,取不到
字符串也能切片
字符串遵循同样规则,切片返回新字符串,原字符串不变。
s = "hello, python"
print(s[:5]) # hello
print(s[-6:]) # python,负数下标从尾部往前数
print(s[::-1]) # nohtyp ,olleh(反转)
负数下标是个实用细节:-1 表示最后一项,-6 表示倒数第 6 项。start 或 end 填负数就是"从尾部数",注意是从 -1 开始,没有 -0。
切片还能赋值
切片不只是读,还能整段替换,长度甚至可以和原来不一样:
nums = [1, 2, 3, 4, 5]
nums[1:3] = [20, 30, 35] # 把下标 1、2 换成三个元素
print(nums) # [1, 20, 30, 35, 4, 5]
nums[:2] = [] # 删掉前两个
print(nums) # [30, 35, 4, 5]
这个用法在批处理列表时很好使,比一段段 append 或 del 省事。
小结
- 切片格式
seq[start:end:step],包头不包尾,越界不报错 - 省略 start 从头取,省略 end 取到尾,
seq[:]是快速浅拷贝 - step 控制步长,
[::-1]一行反转序列;step 为负时从后往前取 - 负数下标从尾部往前数,
-1是最后一项 - 切片可以整段赋值替换,长度可变
把这几个套路练熟,处理列表和字符串会顺手很多。