切片
切片是Python最强大的特性之一,让我们能够高效地访问和操作序列(如列表、元组、字符串)中的元素。
索引体系
Python的索引体系分为正向索引和负向索引两种:
┌─────────────────────────────────────────────────────────────────────┐ │ Python 索引体系图解 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ 列表: ['P', 'y', 't', 'h', 'o', 'n'] │ │ [0] [1] [2] [3] [4] [5] ← 正向索引 │ │ [-6] [-5] [-4] [-3] [-2] [-1] ← 负向索引 │ │ │ │ 正向索引从 0 开始,从左到右依次递增 │ │ 负向索引从 -1 开始,从右到左依次递减 │ │ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ │ │ │ │ -6 -5 -4 -3 -2 -1 │ │ │ │ ↓ ↓ ↓ ↓ ↓ ↓ │ │ │ │ [P] [y] [t] [h] [o] [n] │ │ │ │ ↓ ↓ ↓ ↓ ↓ ↓ │ │ │ │ 0 1 2 3 4 5 │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ │ │ 小技巧: │ │ • s[0] 永远指向第一个元素 │ │ • s[-1] 永远指向最后一个元素 │ │ • s[n] 和 s[n+len(s)] 在范围内时是等价的 │ │ │ └─────────────────────────────────────────────────────────────────────┘
Python
s = ['P', 'y', 't', 'h', 'o', 'n']
print(s[0]) # 'P' - 第一个元素
print(s[5]) # 'n' - 第六个元素
print(s[-1]) # 'n' - 最后一个元素
print(s[-6]) # 'P' - 第一个元素
print(s[-6]) == s[0]) # True - 同一个元素
基本切片语法
切片语法:s[start:end],返回从start到end-1的元素组成的子序列。
┌─────────────────────────────────────────────────────────────────────┐ │ 切片原理图解 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ s = ['P', 'y', 't', 'h', 'o', 'n'] │ │ │ │ s[1:4] 的切分过程: │ │ │ │ ┌───┬───┬───┬───┬───┬───┐ │ │ │ P │ y │ t │ h │ o │ n │ │ │ └───┴───┴───┴───┴───┴───┘ │ │ 0 1 2 3 4 5 ← 索引 │ │ ↑ ↑ │ │ start end (不包含!) │ │ │ │ 结果: ['y', 't', 'h'] ← 索引 1, 2, 3 │ │ │ │ 重要: end 位置的元素不包含在切片中! │ │ │ └─────────────────────────────────────────────────────────────────────┘
Python
s = ['P', 'y', 't', 'h', 'o', 'n']
# 基本切片 [start:end]
print(s[1:4]) # ['y', 't', 'h'] - 索引1,2,3
print(s[0:3]) # ['P', 'y', 't'] - 索引0,1,2
print(s[2:5]) # ['t', 'h', 'o'] - 索引2,3,4
# 省略 start(默认为0)
print(s[:3]) # ['P', 'y', 't'] - 等同于 s[0:3]
# 省略 end(默认为序列长度)
print(s[3:]) # ['h', 'o', 'n'] - 等同于 s[3:6]
# start 和 end 都省略
print(s[:]) # ['P', 'y', 't', 'h', 'o', 'n'] - 复制整个序列
负索引切片
┌─────────────────────────────────────────────────────────────────────┐ │ 负索引切片图解 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ s = ['P', 'y', 't', 'h', 'o', 'n'] │ │ │ │ -6 -5 -4 -3 -2 -1 │ │ ↓ ↓ ↓ ↓ ↓ ↓ │ │ ┌───┬───┬───┬───┬───┬───┐ │ │ │ P │ y │ t │ h │ o │ n │ │ │ └───┴───┴───┴───┴───┴───┘ │ │ 0 1 2 3 4 5 │ │ │ │ s[-4:-1] = ['t', 'h', 'o'] ← 索引 -4, -3, -2 │ │ │ │ s[0:-1] = ['P', 'y', 't', 'h', 'o'] ← 去掉最后一个 │ │ │ │ s[-3:] = ['h', 'o', 'n'] ← 最后三个元素 │ │ │ └─────────────────────────────────────────────────────────────────────┘
Python
s = ['P', 'y', 't', 'h', 'o', 'n']
# 负索引切片
print(s[-4:-1]) # ['t', 'h', 'o'] - 索引 -4, -3, -2
print(s[-3:]) # ['h', 'o', 'n'] - 最后三个
print(s[:-2]) # ['P', 'y', 't', 'h'] - 去掉最后两个
# 混合使用正负索引
print(s[1:-1]) # ['y', 't', 'h', 'o'] - 去掉首尾
print(s[-5:3]) # ['y', 't'] - 索引 -5,-4,1,2
步进切片
切片语法:s[start:end:step],step表示步长,默认为1。
┌─────────────────────────────────────────────────────────────────────┐ │ 步进切片图解 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ s = ['P', 'y', 't', 'h', 'o', 'n'] │ │ [0] [1] [2] [3] [4] [5] │ │ │ │ s[::2] 步长为2,每隔一个取一个: │ │ │ │ ┌───┬───┬───┬───┬───┬───┐ │ │ │ P │ y │ t │ h │ o │ n │ │ │ └───┴───┴───┴───┴───┴───┘ │ │ ↑ ↑ ↑ │ │ [0] [2] [4] ← 索引 0, 2, 4 │ │ │ │ 结果: ['P', 't', 'o'] │ │ │ │ s[::-1] 步长为-1,反向取值: │ │ │ │ ┌───┬───┬───┬───┬───┬───┐ │ │ │ P │ y │ t │ h │ o │ n │ │ │ └───┴───┴───┴───┴───┴───┘ │ │ ↑ ↑ ↑ │ │ [5] [3] [1] ← 索引 5, 3, 1 │ │ │ │ 结果: ['n', 'h', 't', 'y', 'P'] │ │ │ └─────────────────────────────────────────────────────────────────────┘
Python
s = ['P', 'y', 't', 'h', 'o', 'n']
# 步进切片 [start:end:step]
print(s[::2]) # ['P', 't', 'o'] - 每隔一个
print(s[1::2]) # ['y', 'h', 'n'] - 从索引1开始,每隔一个
print(s[::3]) # ['P', 'h'] - 每隔三个
# 反转序列 (常用技巧!)
print(s[::-1]) # ['n', 'o', 'h', 't', 'y', 'P'] - 完全反转
# 负步长切片
print(s[5:2:-1]) # ['n', 'o', 'h'] - 从索引5到3,反向取
print(s[-1:-4:-1]) # ['n', 'o', 'h'] - 从最后一个到索引-4
切片边界计算公式
对于正向切片 s[i:j:step]:
- 实际索引范围: i, i+step, i+2*step, ... 直到 j-1
- 对于 step=1: i 到 j-1
- 对于 step=-1: i 到 j+1(因为是递减)
切片赋值
切片不仅用于读取,还可用于赋值,实现批量修改元素。
┌─────────────────────────────────────────────────────────────────────┐ │ 切片赋值图解 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ 原列表: nums = [1, 2, 3, 4, 5] │ │ │ │ ┌───┬───┬───┬───┬───┐ │ │ │ 1 │ 2 │ 3 │ 4 │ 5 │ │ │ └───┴───┴───┴───┴───┘ │ │ ↑─────↑ │ │ [1:4] ← 要替换的范围 │ │ │ │ nums[1:4] = [10, 20, 30] 后: │ │ │ │ ┌───┬───┬───┬───┬───┬───┐ │ │ │ 1 │10 │20 │30 │ 5 │ │ │ │ └───┴───┴───┴───┴───┴───┘ │ │ ↑─────↑ │ │ 替换为 3 个元素,列表自动调整大小 │ │ │ │ 重要: 替换的元素个数可以和切片长度不同! │ │ │ └─────────────────────────────────────────────────────────────────────┘
Python
nums = [1, 2, 3, 4, 5]
# 替换部分元素 (长度相等)
nums[1:4] = [10, 20, 30]
print(nums) # [1, 10, 20, 30, 5]
# 替换为更多元素 (列表变长)
nums = [1, 2, 3, 4, 5]
nums[1:3] = [10, 20, 30, 40]
print(nums) # [1, 10, 20, 30, 40, 4, 5]
# 替换为更少元素 (列表变短)
nums = [1, 2, 3, 4, 5]
nums[1:4] = [100]
print(nums) # [1, 100, 5]
# 删除元素 (赋值为空列表)
nums = [1, 2, 3, 4, 5]
nums[1:4] = []
print(nums) # [1, 5]
# 插入元素 (空切片赋值)
nums = [1, 5]
nums[1:1] = [2, 3, 4]
print(nums) # [1, 2, 3, 4, 5]
切片复制 vs 引用
理解切片的复制行为非常重要:
┌─────────────────────────────────────────────────────────────────────┐ │ 切片复制 vs 引用图解 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ 简单赋值 (共享引用): │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ original = [1, 2, 3] │ │ │ │ copy_ref = original # 不是复制,只是引用同一个对象! │ │ │ │ │ │ │ │ ┌─────────────┐ │ │ │ │ │ original ───┼───▶ [1, 2, 3] ← 同一个对象 │ │ │ │ │ copy_ref ───┼───↗ │ │ │ │ └─────────────┘ ↗ │ │ │ │ │ │ │ │ copy_ref.append(4) # 同时影响 original! │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ │ │ 切片复制 (独立副本): │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ original = [1, 2, 3] │ │ │ │ copy_list = original[:] # 创建新的副本 │ │ │ │ │ │ │ │ ┌─────────────┐ ┌─────────────┐ │ │ │ │ │ original ───┼───▶ [1, 2, 3] │ │ │ │ │ └─────────────┘ └─────────────┘ │ │ │ │ ┌─────────────┐ ┌─────────────┐ │ │ │ │ │ copy_list ───┼───▶ [1, 2, 3] ←── 独立的新对象! │ │ │ │ └─────────────┘ └─────────────┘ │ │ │ │ │ │ │ │ copy_list.append(4) # 不影响 original │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ │ │ 注意: 切片复制只做浅拷贝,如果列表嵌套可变对象,会有问题 │ │ │ └─────────────────────────────────────────────────────────────────────┘
Python
# 简单赋值 (共享引用)
original = [1, 2, 3]
copy_ref = original
copy_ref.append(4)
print(original) # [1, 2, 3, 4] - original也变了!
# 切片复制 (独立副本)
original = [1, 2, 3]
copy_list = original[:]
copy_list.append(4)
print(original) # [1, 2, 3] - original不变!
print(copy_list) # [1, 2, 3, 4]
浅拷贝问题
切片复制只做浅拷贝,即复制第一层元素。如果列表中嵌套了可变对象(如嵌套列表),内部对象仍然是共享引用:
original = [[1, 2], [3, 4]]
copy_list = original[:]
copy_list[0].append(5) # 修改嵌套列表!
print(original) # [[1, 2, 5], [3, 4]] - 内部列表被修改!
实用技巧
Python
# 1. 复制整个列表
s = [1, 2, 3]
s_copy = s[:]
# 2. 前n个元素
s = ['a', 'b', 'c', 'd', 'e']
print(s[:3]) # ['a', 'b', 'c']
# 3. 后n个元素
print(s[-2:]) # ['d', 'e']
# 4. 判断回文 (常用!)
text = "上海自来水来自海上"
if text == text[::-1]:
print("是回文")
# 5. 每n个元素取一个
s = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(s[::3]) # [1, 4, 7, 10] - 每3个取一个
# 6. 反转字符串/列表
text = "Python"
print(text[::-1]) # "nohtyP"
# 7. 去除首尾空格/元素
s = [0, 1, 2, 0]
print(s[1:-1]) # [1, 2] - 去掉首尾的0
slice() 函数
Python内置slice()函数可以创建切片对象,便于重复使用。
Python
s = ['P', 'y', 't', 'h', 'o', 'n']
# 创建切片对象
my_slice = slice(1, 4)
print(s[my_slice]) # ['y', 't', 'h']
# 带步长的切片对象
step_slice = slice(0, 6, 2)
print(s[step_slice]) # ['P', 't', 'o']
# 只传两个参数
prefix_slice = slice(None, 3) # 等同于 [:3]
print(s[prefix_slice]) # ['P', 'y', 't']
# 实用场景: 处理数据列
data = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
col_slice = slice(1, 3) # 取第2、3列
for row in data:
print(row[col_slice]) # [2, 3], [5, 6], [8, 9]
元组切片
切片同样适用于元组。元组切片返回新的元组。
Python
t = ('P', 'y', 't', 'h', 'o', 'n')
# 元组切片 (返回新元组)
print(t[1:4]) # ('y', 't', 'h')
print(t[::2]) # ('P', 't', 'o')
print(t[::-1]) # ('n', 'o', 'h', 't', 'y', 'P')
章节测验
问题1: s = [0,1,2,3,4,5],s[1:4]的结果是?
问题2: s[::-1]是什么意思?
问题3: nums[1:3] = [10, 20]会替换多少个元素?
问题4: 切片副本和原始列表的关系是?
练习题
练习1: 去除首尾空格
使用切片去除字符串首尾的空格。
s = " hello "
# 去除首尾空格
练习2: 分块处理
将列表每3个元素分成一组。
s = [1, 2, 3, 4, 5, 6, 7]
# 每3个一组: [[1,2,3], [4,5,6], [7]]
练习3: 奇偶分离
使用切片将列表的奇数位和偶数位分开。
s = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# 偶数位: [0, 2, 4, 6, 8]
# 奇数位: [1, 3, 5, 7, 9]
🐍 Python 在线代码编辑器
📝 运行结果