Python 切片(Slice)用法详解
切片用于从序列(字符串、列表、元组等)中提取子序列。
一、基础语法
sequence[start:stop:step]
| 参数 | 说明 | 默认值 |
|---|---|---|
start | 起始索引(包含) | 0 |
stop | 结束索引(不包含) | 序列长度 |
step | 步长(正数正向,负数反向) | 1 |
核心口诀:左闭右开,步长可正可负
二、基础切片操作
1. 基本用法
s = "Hello, World!"
s[0:5] # 'Hello' 从索引0到4
s[7:12] # 'World' 从索引7到11
s[:5] # 'Hello' 从头开始到索引4
s[7:] # 'World!' 从索引7到末尾
s[:] # 'Hello, World!' 复制整个序列
2. 带步长的切片
nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
nums[::2] # [0, 2, 4, 6, 8] 每隔一个取一个
nums[1::2] # [1, 3, 5, 7, 9] 从索引1开始,每隔一个
nums[::3] # [0, 3, 6, 9] 每隔两个取一个
3. 负数索引
s = "Python"
s[-1] # 'n' 最后一个字符
s[-3:] # 'hon' 最后3个字符
s[:-3] # 'Pyt' 除去最后3个
s[-4:-1] # 'tho' 倒数第4到倒数第2
三、高级技巧
1. 反向切片(步长为负)
s = "Python"
s[::-1] # 'nohtyP' 字符串反转
s[5:2:-1] # 'noh' 从索引5到3,反向
s[-1::-1] # 'nohtyP' 从最后一个到开头,反向
2. 常见快捷操作
lst = [1, 2, 3, 4, 5]
# 复制列表(浅拷贝)
new_lst = lst[:]
# 获取最后 N 个元素
last_3 = lst[-3:] # [3, 4, 5]
# 去掉最后 N 个元素
except_last = lst[:-2] # [1, 2, 3]
# 反转并每隔一个取一个
lst[::-2] # [5, 3, 1]
四、业务实战场景
1. 字符串处理
文件扩展名提取
filename = "document.pdf"
extension = filename[-4:] # '.pdf'
# 更安全的做法(处理多段扩展名)
filename = "archive.tar.gz"
base, ext = filename.rsplit('.', 1) # base='archive.tar', ext='gz'
隐藏敏感信息
def mask_phone(phone: str) -> str:
"""手机号脱敏:138****8888"""
return phone[:3] + "****" + phone[-4:]
def mask_email(email: str) -> str:
"""邮箱脱敏:k***@example.com"""
local, domain = email.split('@')
return local[0] + "***@" + domain
mask_phone("13812348888") # '138****8888'
mask_email("kim@example.com") # 'k***@example.com'
URL 解析
url = "https://api.example.com/v1/users?page=1&limit=10"
protocol = url[:url.find("://")] # 'https'
path_start = url.find("/", 8)
path = url[path_start:url.find("?")] # '/v1/users'
query = url[url.find("?")+1:] # 'page=1&limit=10'
2. 列表分页处理
def paginate(items: list, page: int, page_size: int = 10) -> list:
"""列表分页"""
start = (page - 1) * page_size
end = start + page_size
return items[start:end]
data = list(range(1, 101)) # 100条数据
paginate(data, 1, 10) # [1, 2, ..., 10]
paginate(data, 3, 10) # [21, 22, ..., 30]
3. 数据清洗与采样
# 去掉 CSV 表头
rows = csv_data.split('\n')[1:]
# 每隔 N 行采样
sample = data[::5] # 每第5条数据
# 滑动窗口(取最近N条)
recent = logs[-100:] # 最近100条日志
# 去掉首尾标记
content = html[6:-7] # 去除 <p>...</p> 标签
4. 二进制数据处理
# 解析固定格式的二进制包头
packet = b'\x01\x02\x03\x04\x05\x06\x07\x08'
type_field = packet[0:2] # 类型字段
length_field = packet[2:4] # 长度字段
payload = packet[4:] # 数据部分
5. 批量数据移动
def rotate_left(lst: list, k: int) -> list:
"""列表左旋 k 位"""
k = k % len(lst)
return lst[k:] + lst[:k]
def rotate_right(lst: list, k: int) -> list:
"""列表右旋 k 位"""
k = k % len(lst)
return lst[-k:] + lst[:-k]
nums = [1, 2, 3, 4, 5]
rotate_left(nums, 2) # [3, 4, 5, 1, 2]
rotate_right(nums, 2) # [4, 5, 1, 2, 3]
五、切片赋值(仅可变序列)
1. 替换元素
lst = [1, 2, 3, 4, 5]
lst[1:3] = [20, 30] # [1, 20, 30, 4, 5]
# 不等长替换
lst[1:3] = [200, 300, 400] # [1, 200, 300, 400, 4, 5]
2. 删除元素
lst = [1, 2, 3, 4, 5]
lst[1:3] = [] # [1, 4, 5] 删除索引1和2
del lst[::2] # [4] 删除偶数索引的元素
3. 插入元素
lst = [1, 4, 5]
lst[1:1] = [2, 3] # [1, 2, 3, 4, 5] 在索引1处插入
4. 批量替换(步长匹配)
lst = [1, 2, 3, 4, 5, 6]
lst[::2] = [10, 30, 50] # [10, 2, 30, 4, 50, 6]
# 注意:步长不为1时,左右元素数量必须相等
六、切片对象 slice()
当需要重复使用相同的切片规则时,可以使用 slice() 对象:
# 定义切片规则
first_three = slice(None, 3) # 等价于 [:3]
every_second = slice(None, None, 2) # 等价于 [::2]
last_five = slice(-5, None) # 等价于 [-5:]
# 应用切片
s = "Hello, World!"
s[first_three] # 'Hel'
s[every_second] # 'Hlo ol!'
# 获取切片信息
print(first_three.start) # None
print(first_three.stop) # 3
print(first_three.step) # None
动态切片场景
def extract_columns(data: list[list], col_indices: slice) -> list:
"""从二维数据中按列提取"""
return [row[col_indices] for row in data]
# 提取前3列
table = [
['Name', 'Age', 'City', 'Salary'],
['Alice', '25', 'NYC', '50000'],
['Bob', '30', 'LA', '60000'],
]
extract_columns(table[1:], slice(0, 3))
# [['Alice', '25', 'NYC'], ['Bob', '30', 'LA']]
七、常见陷阱与注意事项
1. 切片越界不报错
lst = [1, 2, 3]
lst[5:10] # [] 不报错,返回空列表
lst[100] # IndexError! 索引访问会报错
原则:切片越界是安全的,索引越界会报错
2. 切片返回新对象(浅拷贝)
original = [[1], [2], [3]]
copy = original[:]
copy[0][0] = 999
print(original) # [[999], [2], [3]] 原列表也被修改了!
需要深拷贝时使用 copy.deepcopy()
3. 步长为负数时的索引顺序
s = "Python"
s[3:0:-1] # 'hty' 正确:从3到1反向
s[0:3:-1] # '' 错误:start < stop 且 step 为负,结果为空
4. 字符串切片不可赋值
s = "Hello"
s[0] = "h" # TypeError! 字符串不可变
# 正确做法:切片组合新字符串
s = "h" + s[1:] # 'hello'
八、性能提示
| 操作 | 时间复杂度 | 说明 |
|---|---|---|
lst[a:b] | O(k) | k = b-a,需要创建新列表 |
lst[:] | O(n) | 浅拷贝整个列表 |
lst[::2] | O(n) | 遍历整个序列 |
优化建议:
- 需要频繁访问子序列时,考虑使用
itertools.islice(惰性求值) - 大数据集避免频繁切片,尽量使用生成器
from itertools import islice
# 处理大文件时,避免一次性切片
with open('large_file.txt') as f:
for line in islice(f, 100, 200): # 只读取100-199行
process(line)
九、速查表
| 需求 | 切片写法 |
|---|---|
| 复制序列 | seq[:] |
| 反转序列 | seq[::-1] |
| 前 N 个 | seq[:N] |
| 后 N 个 | seq[-N:] |
| 去掉前 N 个 | seq[N:] |
| 去掉后 N 个 | seq[:-N] |
| 每隔一个 | seq[::2] |
| 奇数位置 | seq[1::2] |
| 最后 N 个反转 | seq[:-N-1:-1] |
📌 本文是 python-basics 的专题展开。