文章
合集Python 语言基础第 4 / 21 篇

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 的专题展开。