写 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 是最后一项
  • 切片可以整段赋值替换,长度可变

把这几个套路练熟,处理列表和字符串会顺手很多。