Python 中的切片详解
1. 前言
1.1 什么是切片(Slicing)
切片(Slicing)是 Python 中一种非常强大且常用的操作,它允许你从序列类型(如字符串、列表、元组等)中提取一部分元素,而不需要修改原始数据。切片操作返回一个新的序列,原始数据保持不变。
1.2 为什么需要学习切片
- 提高效率:快速获取序列的一部分,无需循环
- 代码简洁:一行代码就能完成复杂的提取操作
- 功能强大:支持正索引、负索引、步长等多种操作
- 广泛应用:在数据处理、字符串操作、列表处理中经常使用
1.3 哪些数据类型支持切片
Python 中支持切片操作的数据类型包括:
- 字符串(str):
"Hello World" - 列表(list):
[1, 2, 3, 4, 5] - 元组(tuple):
(1, 2, 3, 4, 5) - 字节序列(bytes):
b'Hello' - 字节数组(bytearray):
bytearray(b'Hello') - 范围(range):
range(10)(Python 3.2+)
2. 切片的基本语法
2.1 基本语法格式
切片的语法格式如下:
sequence[start:stop:step]
参数说明:
start:起始索引(包含),默认为序列的开始(0)stop:结束索引(不包含),默认为序列的结束step:步长(可选),默认为 1,表示每次取一个元素
重要提示:
- 切片操作返回的是新的序列,不会修改原始数据
- 结束索引是不包含的,即
[start:stop]取的是从start到stop-1的元素
2.2 最简单的切片示例
# 创建一个列表
my_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# 获取索引 2 到 5 的元素(不包含索引 5)
result = my_list[2:5]
print(result) # 输出: [2, 3, 4]
# 原始列表不变
print(my_list) # 输出: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
3. 索引的基础知识
3.1 正索引(正向索引)
正索引从 0 开始,从左到右递增:
my_list = ['a', 'b', 'c', 'd', 'e']
# 0 1 2 3 4 <- 正索引
print(my_list[0]) # 输出: 'a'
print(my_list[1]) # 输出: 'b'
print(my_list[4]) # 输出: 'e'
3.2 负索引(反向索引)
负索引从 -1 开始,从右到左递减:
my_list = ['a', 'b', 'c', 'd', 'e']
# -5 -4 -3 -2 -1 <- 负索引
print(my_list[-1]) # 输出: 'e'(最后一个元素)
print(my_list[-2]) # 输出: 'd'(倒数第二个元素)
print(my_list[-5]) # 输出: 'a'(第一个元素)
3.3 索引示意图
正索引: 0 1 2 3 4 5 6 7 8 9
序列: [a] [b] [c] [d] [e] [f] [g] [h] [i] [j]
负索引: -10 -9 -8 -7 -6 -5 -4 -3 -2 -1
4. 切片的各种用法
4.1 基本切片:[start:stop]
语法:sequence[start:stop]
从索引 start 开始,到索引 stop-1 结束(不包含 stop)。
# 示例 1:使用正索引
my_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(my_list[2:6]) # 输出: [2, 3, 4, 5]
print(my_list[0:3]) # 输出: [0, 1, 2]
print(my_list[5:10]) # 输出: [5, 6, 7, 8, 9]
# 示例 2:使用负索引
print(my_list[-5:-1]) # 输出: [5, 6, 7, 8]
print(my_list[-3:-1]) # 输出: [7, 8]
# 示例 3:混合使用正负索引
print(my_list[2:-2]) # 输出: [2, 3, 4, 5, 6, 7]
print(my_list[-5:8]) # 输出: [5, 6, 7]
重要提示:
- 如果
start >= stop,返回空序列 - 如果索引超出范围,不会报错,会自动调整到有效范围
my_list = [0, 1, 2, 3, 4]
print(my_list[3:1]) # 输出: [](空列表,因为 3 >= 1)
print(my_list[1:100]) # 输出: [1, 2, 3, 4](自动调整到列表末尾)
print(my_list[-100:3]) # 输出: [0, 1, 2](自动调整到列表开头)
4.2 省略起始索引:[:stop]
语法:sequence[:stop]
从序列开头到索引 stop-1 结束。
my_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(my_list[:5]) # 输出: [0, 1, 2, 3, 4](前 5 个元素)
print(my_list[:3]) # 输出: [0, 1, 2](前 3 个元素)
print(my_list[:0]) # 输出: [](空列表)
# 使用负索引
print(my_list[:-3]) # 输出: [0, 1, 2, 3, 4, 5, 6](除了最后 3 个元素)
print(my_list[:-1]) # 输出: [0, 1, 2, 3, 4, 5, 6, 7, 8](除了最后一个元素)
4.3 省略结束索引:[start:]
语法:sequence[start:]
从索引 start 开始到序列末尾。
my_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(my_list[5:]) # 输出: [5, 6, 7, 8, 9](从索引 5 到末尾)
print(my_list[3:]) # 输出: [3, 4, 5, 6, 7, 8, 9]
print(my_list[0:]) # 输出: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9](整个列表)
# 使用负索引
print(my_list[-3:]) # 输出: [7, 8, 9](最后 3 个元素)
print(my_list[-1:]) # 输出: [9](最后一个元素)
4.4 省略起始和结束索引:[:]
语法:sequence[:]
获取整个序列的副本(浅拷贝)。
my_list = [0, 1, 2, 3, 4, 5]
# 获取整个列表的副本
copy_list = my_list[:]
print(copy_list) # 输出: [0, 1, 2, 3, 4, 5]
# 修改副本不会影响原列表
copy_list[0] = 999
print(copy_list) # 输出: [999, 1, 2, 3, 4, 5]
print(my_list) # 输出: [0, 1, 2, 3, 4, 5](原列表不变)
注意:对于列表,[:] 是浅拷贝。如果列表包含可变对象(如嵌套列表),需要深拷贝。
# 浅拷贝示例
original = [[1, 2], [3, 4]]
shallow_copy = original[:]
# 修改浅拷贝中的嵌套列表会影响原列表
shallow_copy[0][0] = 999
print(original) # 输出: [[999, 2], [3, 4]]
print(shallow_copy) # 输出: [[999, 2], [3, 4]]
4.5 带步长的切片:[start:stop:step]
语法:sequence[start:stop:step]
从 start 开始,每隔 step 个元素取一个,直到 stop-1。
my_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# 步长为 2:每隔一个元素取一个
print(my_list[0:10:2]) # 输出: [0, 2, 4, 6, 8]
print(my_list[1:10:2]) # 输出: [1, 3, 5, 7, 9]
# 步长为 3:每隔两个元素取一个
print(my_list[0:10:3]) # 输出: [0, 3, 6, 9]
print(my_list[1:10:3]) # 输出: [1, 4, 7]
# 省略起始和结束,只指定步长
print(my_list[::2]) # 输出: [0, 2, 4, 6, 8](整个列表,步长为 2)
print(my_list[::3]) # 输出: [0, 3, 6, 9](整个列表,步长为 3)
4.6 负步长切片:反向提取
语法:sequence[start:stop:step](step 为负数)
当步长为负数时,切片会从右向左提取元素。
my_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# 步长为 -1:反转整个列表
print(my_list[::-1]) # 输出: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
# 步长为 -2:从右向左,每隔一个元素取一个
print(my_list[::-2]) # 输出: [9, 7, 5, 3, 1]
# 指定起始和结束位置(注意:使用负步长时,start 应该大于 stop)
print(my_list[8:2:-1]) # 输出: [8, 7, 6, 5, 4, 3]
print(my_list[9:0:-2]) # 输出: [9, 7, 5, 3, 1]
# 从末尾到开头
print(my_list[9::-1]) # 输出: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
print(my_list[:3:-1]) # 输出: [9, 8, 7, 6, 5, 4](从末尾到索引 3,不包含索引 3)
重要提示:
- 使用负步长时,
start必须大于stop,否则返回空序列 - 使用负步长时,索引的含义会发生变化
my_list = [0, 1, 2, 3, 4]
# 错误示例:start < stop,返回空列表
print(my_list[1:4:-1]) # 输出: []
# 正确示例:start > stop
print(my_list[4:1:-1]) # 输出: [4, 3, 2]
5. 不同数据类型的切片示例
5.1 字符串切片
字符串也支持切片操作,返回新的字符串。
# 基本字符串切片
text = "Hello World"
print(text[0:5]) # 输出: "Hello"
print(text[6:11]) # 输出: "World"
print(text[:5]) # 输出: "Hello"
print(text[6:]) # 输出: "World"
print(text[::-1]) # 输出: "dlroW olleH"(反转字符串)
# 提取子字符串
email = "user@example.com"
username = email[:4] # 输出: "user"
domain = email[5:] # 输出: "example.com"
at_symbol = email[4:5] # 输出: "@"
# 每隔一个字符提取
print(text[::2]) # 输出: "HloWrd"(每隔一个字符)
print(text[1::2]) # 输出: "el ol"(从索引 1 开始,每隔一个字符)
# 反转字符串的几种方法
text = "Python"
print(text[::-1]) # 方法 1:使用切片
print(''.join(reversed(text))) # 方法 2:使用 reversed() 函数
5.2 列表切片
列表切片是最常用的操作之一。
# 基本列表切片
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# 获取前 5 个元素
first_five = numbers[:5]
print(first_five) # 输出: [0, 1, 2, 3, 4]
# 获取后 5 个元素
last_five = numbers[-5:]
print(last_five) # 输出: [5, 6, 7, 8, 9]
# 获取中间的元素
middle = numbers[3:7]
print(middle) # 输出: [3, 4, 5, 6]
# 每隔一个元素取一个
even_indices = numbers[::2]
print(even_indices) # 输出: [0, 2, 4, 6, 8]
odd_indices = numbers[1::2]
print(odd_indices) # 输出: [1, 3, 5, 7, 9]
# 反转列表
reversed_numbers = numbers[::-1]
print(reversed_numbers) # 输出: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
5.3 元组切片
元组也支持切片,返回新的元组。
# 元组切片
my_tuple = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
print(my_tuple[2:6]) # 输出: (2, 3, 4, 5)
print(my_tuple[:5]) # 输出: (0, 1, 2, 3, 4)
print(my_tuple[5:]) # 输出: (5, 6, 7, 8, 9)
print(my_tuple[::-1]) # 输出: (9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
print(my_tuple[::2]) # 输出: (0, 2, 4, 6, 8)
5.4 字符串、列表、元组的对比
# 字符串
text = "Python"
print(text[1:4]) # 输出: "yth"
print(type(text[1:4])) # 输出: <class 'str'>
# 列表
my_list = ['P', 'y', 't', 'h', 'o', 'n']
print(my_list[1:4]) # 输出: ['y', 't', 'h']
print(type(my_list[1:4])) # 输出: <class 'list'>
# 元组
my_tuple = ('P', 'y', 't', 'h', 'o', 'n')
print(my_tuple[1:4]) # 输出: ('y', 't', 'h')
print(type(my_tuple[1:4])) # 输出: <class 'tuple'>
6. 切片的常见应用场景
6.1 获取序列的前 N 个元素
my_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 获取前 3 个元素
first_three = my_list[:3]
print(first_three) # 输出: [1, 2, 3]
# 获取前 5 个元素
first_five = my_list[:5]
print(first_five) # 输出: [1, 2, 3, 4, 5]
6.2 获取序列的后 N 个元素
my_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 获取后 3 个元素
last_three = my_list[-3:]
print(last_three) # 输出: [8, 9, 10]
# 获取后 5 个元素
last_five = my_list[-5:]
print(last_five) # 输出: [6, 7, 8, 9, 10]
6.3 获取序列的中间部分
my_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 获取中间的元素(去掉第一个和最后一个)
middle = my_list[1:-1]
print(middle) # 输出: [2, 3, 4, 5, 6, 7, 8, 9]
# 获取索引 2 到 7 的元素
middle_part = my_list[2:8]
print(middle_part) # 输出: [3, 4, 5, 6, 7, 8]
6.4 反转序列
# 反转字符串
text = "Python"
reversed_text = text[::-1]
print(reversed_text) # 输出: "nohtyP"
# 反转列表
my_list = [1, 2, 3, 4, 5]
reversed_list = my_list[::-1]
print(reversed_list) # 输出: [5, 4, 3, 2, 1]
# 反转元组
my_tuple = (1, 2, 3, 4, 5)
reversed_tuple = my_tuple[::-1]
print(reversed_tuple) # 输出: (5, 4, 3, 2, 1)
6.5 提取偶数索引或奇数索引的元素
my_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# 提取偶数索引的元素(索引 0, 2, 4, 6, 8)
even_indices = my_list[::2]
print(even_indices) # 输出: [0, 2, 4, 6, 8]
# 提取奇数索引的元素(索引 1, 3, 5, 7, 9)
odd_indices = my_list[1::2]
print(odd_indices) # 输出: [1, 3, 5, 7, 9]
6.6 字符串处理:提取文件名、扩展名等
# 提取文件名(不含扩展名)
file_path = "document.pdf"
# 找到最后一个点的位置
dot_index = file_path.rfind('.')
if dot_index != -1:
filename = file_path[:dot_index]
extension = file_path[dot_index+1:]
print(f"文件名: {filename}") # 输出: 文件名: document
print(f"扩展名: {extension}") # 输出: 扩展名: pdf
# 提取 URL 的不同部分
url = "https://www.example.com/page"
protocol = url[:5] # "https"
domain = url[8:21] # "www.example.com"
path = url[21:] # "/page"
# 提取日期部分
date_string = "2024-01-15"
year = date_string[:4] # "2024"
month = date_string[5:7] # "01"
day = date_string[8:] # "15"
6.7 列表的分块处理
# 将列表分成固定大小的块
def chunk_list(lst, chunk_size):
"""将列表分成指定大小的块"""
return [lst[i:i+chunk_size] for i in range(0, len(lst), chunk_size)]
my_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 分成每块 3 个元素
chunks = chunk_list(my_list, 3)
print(chunks) # 输出: [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]
# 分成每块 4 个元素
chunks = chunk_list(my_list, 4)
print(chunks) # 输出: [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10]]
6.8 删除序列的某些元素(通过切片创建新序列)
my_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 删除第一个元素
without_first = my_list[1:]
print(without_first) # 输出: [2, 3, 4, 5, 6, 7, 8, 9, 10]
# 删除最后一个元素
without_last = my_list[:-1]
print(without_last) # 输出: [1, 2, 3, 4, 5, 6, 7, 8, 9]
# 删除前两个元素
without_first_two = my_list[2:]
print(without_first_two) # 输出: [3, 4, 5, 6, 7, 8, 9, 10]
# 删除后两个元素
without_last_two = my_list[:-2]
print(without_last_two) # 输出: [1, 2, 3, 4, 5, 6, 7, 8]
# 删除第一个和最后一个元素
without_ends = my_list[1:-1]
print(without_ends) # 输出: [2, 3, 4, 5, 6, 7, 8, 9]
7. 切片赋值(修改序列)
7.1 列表的切片赋值
切片不仅可以读取,还可以用于修改列表(注意:字符串和元组不支持切片赋值,因为它们是不可变的)。
# 替换列表的一部分
my_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# 替换索引 2 到 5 的元素
my_list[2:6] = [20, 30, 40, 50]
print(my_list) # 输出: [0, 1, 20, 30, 40, 50, 6, 7, 8, 9]
# 替换的元素数量可以不同
my_list = [0, 1, 2, 3, 4, 5]
my_list[2:4] = [20, 30, 40] # 用 3 个元素替换 2 个元素
print(my_list) # 输出: [0, 1, 20, 30, 40, 4, 5]
my_list = [0, 1, 2, 3, 4, 5]
my_list[2:5] = [20] # 用 1 个元素替换 3 个元素
print(my_list) # 输出: [0, 1, 20, 5]
# 删除元素:用空列表替换
my_list = [0, 1, 2, 3, 4, 5]
my_list[2:4] = []
print(my_list) # 输出: [0, 1, 4, 5]
# 插入元素:在指定位置插入
my_list = [0, 1, 2, 3, 4, 5]
my_list[2:2] = [20, 30] # 在索引 2 的位置插入
print(my_list) # 输出: [0, 1, 20, 30, 2, 3, 4, 5]
7.2 字符串和元组不支持切片赋值
# 字符串不支持切片赋值(字符串是不可变的)
text = "Hello"
# text[0:2] = "Hi" # 这会报错:TypeError: 'str' object does not support item assignment
# 元组不支持切片赋值(元组是不可变的)
my_tuple = (1, 2, 3, 4, 5)
# my_tuple[0:2] = (10, 20) # 这会报错:TypeError: 'tuple' object does not support item assignment
8. 切片的边界情况
8.1 索引超出范围
切片操作不会因为索引超出范围而报错,会自动调整到有效范围。
my_list = [0, 1, 2, 3, 4]
# 索引超出范围,自动调整
print(my_list[1:100]) # 输出: [1, 2, 3, 4](自动调整到列表末尾)
print(my_list[-100:3]) # 输出: [0, 1, 2](自动调整到列表开头)
print(my_list[-100:100]) # 输出: [0, 1, 2, 3, 4](整个列表)
# 空切片
print(my_list[10:20]) # 输出: [](空列表,因为起始索引超出范围)
8.2 start >= stop 的情况
当 start >= stop 且步长为正数时,返回空序列。
my_list = [0, 1, 2, 3, 4, 5]
print(my_list[3:1]) # 输出: [](空列表)
print(my_list[5:2]) # 输出: [](空列表)
print(my_list[0:0]) # 输出: [](空列表)
# 但是使用负步长时,start 必须大于 stop
print(my_list[3:1:-1]) # 输出: [3, 2](正常,因为步长为负数)
8.3 空序列的切片
对空序列进行切片操作,返回空序列。
empty_list = []
print(empty_list[:]) # 输出: []
print(empty_list[0:5]) # 输出: []
print(empty_list[::-1]) # 输出: []
empty_string = ""
print(empty_string[:]) # 输出: ""
print(empty_string[0:5]) # 输出: ""
print(empty_string[::-1]) # 输出: ""
8.4 步长为 0 的情况
步长不能为 0,否则会报错。
my_list = [0, 1, 2, 3, 4]
# print(my_list[::0]) # 这会报错:ValueError: slice step cannot be zero
9. 切片的高级技巧
9.1 使用变量作为切片参数
my_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# 使用变量
start = 2
stop = 7
step = 2
result = my_list[start:stop:step]
print(result) # 输出: [2, 4, 6]
# 动态计算切片范围
def get_middle_half(lst):
"""获取列表中间一半的元素"""
start = len(lst) // 4
stop = len(lst) - len(lst) // 4
return lst[start:stop]
my_list = list(range(20))
print(get_middle_half(my_list)) # 输出: [5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
9.2 使用 slice 对象
可以使用 slice() 函数创建切片对象,然后用于切片操作。
my_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# 创建切片对象
s = slice(2, 7, 2)
result = my_list[s]
print(result) # 输出: [2, 4, 6]
# slice 对象的参数
s1 = slice(2, 7) # start=2, stop=7, step=None
s2 = slice(2, 7, 2) # start=2, stop=7, step=2
s3 = slice(None, 5) # start=None, stop=5, step=None(相当于 [:5])
s4 = slice(5, None) # start=5, stop=None, step=None(相当于 [5:])
s5 = slice(None, None, -1) # 反转(相当于 [::-1])
print(my_list[s1]) # 输出: [2, 3, 4, 5, 6]
print(my_list[s2]) # 输出: [2, 4, 6]
print(my_list[s3]) # 输出: [0, 1, 2, 3, 4]
print(my_list[s4]) # 输出: [5, 6, 7, 8, 9]
print(my_list[s5]) # 输出: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
# slice 对象的属性
s = slice(2, 7, 2)
print(s.start) # 输出: 2
print(s.stop) # 输出: 7
print(s.step) # 输出: 2
9.3 多维切片的限制
Python 的标准序列类型(列表、元组、字符串)不支持多维切片。但 NumPy 数组支持多维切片。
# 标准列表不支持多维切片
my_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
# print(my_list[0:2, 1:3]) # 这会报错
# 需要先切片行,再切片列
rows = my_list[0:2] # 获取前两行
print(rows) # 输出: [[1, 2, 3], [4, 5, 6]]
# 然后对每一行进行切片
result = [row[1:3] for row in rows]
print(result) # 输出: [[2, 3], [5, 6]]
# NumPy 数组支持多维切片(需要安装 numpy)
# import numpy as np
# arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
# print(arr[0:2, 1:3]) # 输出: [[2, 3], [5, 6]]
9.4 切片的性能考虑
# 切片操作会创建新的序列,对于大列表可能消耗内存
large_list = list(range(1000000))
# 这会创建一个新的列表,占用内存
sliced = large_list[:100] # 只取前 100 个元素
# 如果只需要遍历,可以使用迭代器
from itertools import islice
sliced_iter = islice(large_list, 100) # 返回迭代器,不创建新列表
for item in sliced_iter:
pass # 处理元素
10. 常见错误和注意事项
10.1 混淆索引和切片
my_list = [0, 1, 2, 3, 4, 5]
# 索引:返回单个元素
print(my_list[2]) # 输出: 2(整数)
# 切片:返回序列
print(my_list[2:3]) # 输出: [2](列表,包含一个元素)
# 注意区别
print(type(my_list[2])) # 输出: <class 'int'>
print(type(my_list[2:3])) # 输出: <class 'list'>
10.2 忘记结束索引是不包含的
my_list = [0, 1, 2, 3, 4, 5]
# 错误理解:以为 [2:5] 包含索引 5
# 正确理解:[2:5] 包含索引 2, 3, 4,不包含索引 5
print(my_list[2:5]) # 输出: [2, 3, 4],不包含索引 5 的元素
# 如果要包含索引 5,需要使用 [2:6]
print(my_list[2:6]) # 输出: [2, 3, 4, 5]
10.3 负步长时的索引顺序
my_list = [0, 1, 2, 3, 4, 5]
# 使用负步长时,start 必须大于 stop
print(my_list[5:2:-1]) # 输出: [5, 4, 3](正确)
print(my_list[2:5:-1]) # 输出: [](错误,返回空列表)
10.4 切片不会修改原序列(字符串和元组)
# 字符串和元组是不可变的,切片不会修改原对象
text = "Hello"
new_text = text[::-1]
print(text) # 输出: "Hello"(原字符串不变)
print(new_text) # 输出: "ollH"(新字符串)
# 列表切片也不会修改原列表(除非使用切片赋值)
my_list = [0, 1, 2, 3, 4]
sliced = my_list[1:4]
sliced[0] = 999
print(sliced) # 输出: [999, 2, 3](切片副本被修改)
print(my_list) # 输出: [0, 1, 2, 3, 4](原列表不变)
10.5 浅拷贝的问题
# 列表的切片是浅拷贝
original = [[1, 2], [3, 4]]
shallow_copy = original[:]
# 修改浅拷贝中的嵌套列表会影响原列表
shallow_copy[0][0] = 999
print(original) # 输出: [[999, 2], [3, 4]]
print(shallow_copy) # 输出: [[999, 2], [3, 4]]
# 如果需要深拷贝,使用 copy.deepcopy()
import copy
original = [[1, 2], [3, 4]]
deep_copy = copy.deepcopy(original)
deep_copy[0][0] = 999
print(original) # 输出: [[1, 2], [3, 4]](原列表不变)
print(deep_copy) # 输出: [[999, 2], [3, 4]]
11. 实战练习
11.1 练习 1:提取字符串的不同部分
# 任务:从字符串 "2024-01-15 10:30:45" 中提取年、月、日、时、分、秒
datetime_str = "2024-01-15 10:30:45"
year = datetime_str[:4] # "2024"
month = datetime_str[5:7] # "01"
day = datetime_str[8:10] # "15"
hour = datetime_str[11:13] # "10"
minute = datetime_str[14:16] # "30"
second = datetime_str[17:] # "45"
print(f"年: {year}, 月: {month}, 日: {day}")
print(f"时: {hour}, 分: {minute}, 秒: {second}")
11.2 练习 2:反转字符串中的单词
# 任务:反转字符串 "Hello World Python" 中的每个单词
text = "Hello World Python"
words = text.split() # ['Hello', 'World', 'Python']
reversed_words = [word[::-1] for word in words]
result = ' '.join(reversed_words)
print(result) # 输出: "olleH dlroW nohtyP"
11.3 练习 3:提取列表的特定模式
# 任务:从列表中提取所有偶数索引的元素,然后反转
my_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# 提取偶数索引的元素
even_indices = my_list[::2] # [0, 2, 4, 6, 8]
# 反转
reversed_even = even_indices[::-1] # [8, 6, 4, 2, 0]
print(reversed_even) # 输出: [8, 6, 4, 2, 0]
11.4 练习 4:实现一个简单的分页功能
# 任务:实现一个函数,将列表分成指定大小的页
def paginate(items, page_size, page_num):
"""分页函数
Args:
items: 要分页的列表
page_size: 每页的大小
page_num: 页码(从 1 开始)
Returns:
指定页的元素列表
"""
start = (page_num - 1) * page_size
stop = start + page_size
return items[start:stop]
# 测试
my_list = list(range(1, 21)) # [1, 2, 3, ..., 20]
print(paginate(my_list, 5, 1)) # 输出: [1, 2, 3, 4, 5](第 1 页)
print(paginate(my_list, 5, 2)) # 输出: [6, 7, 8, 9, 10](第 2 页)
print(paginate(my_list, 5, 3)) # 输出: [11, 12, 13, 14, 15](第 3 页)
print(paginate(my_list, 5, 4)) # 输出: [16, 17, 18, 19, 20](第 4 页)
11.5 练习 5:检查字符串是否为回文
# 任务:检查字符串是否为回文(正读和反读都一样)
def is_palindrome(text):
"""检查字符串是否为回文"""
# 转换为小写并移除空格
text = text.lower().replace(' ', '')
# 使用切片反转字符串并比较
return text == text[::-1]
# 测试
print(is_palindrome("level")) # 输出: True
print(is_palindrome("hello")) # 输出: False
print(is_palindrome("A man a plan a canal Panama")) # 输出: True
11.6 练习 6:提取文件扩展名
# 任务:从文件名中提取扩展名
def get_extension(filename):
"""提取文件扩展名"""
# 找到最后一个点的位置
dot_index = filename.rfind('.')
if dot_index != -1 and dot_index < len(filename) - 1:
return filename[dot_index+1:]
return ""
# 测试
print(get_extension("document.pdf")) # 输出: "pdf"
print(get_extension("image.jpg")) # 输出: "jpg"
print(get_extension("script.py")) # 输出: "py"
print(get_extension("no_extension")) # 输出: ""
12. 总结
12.1 切片的核心要点
-
基本语法:
sequence[start:stop:step]start:起始索引(包含)stop:结束索引(不包含)step:步长(可选,默认为 1)
-
重要特性:
- 切片返回新的序列,不修改原序列
- 结束索引是不包含的
- 索引超出范围不会报错,会自动调整
- 支持正索引和负索引
- 支持正步长和负步长
-
常用模式:
[:]:获取整个序列的副本[:n]:获取前 n 个元素[-n:]:获取后 n 个元素[::-1]:反转序列[::2]:每隔一个元素取一个
12.2 适用场景
- 字符串处理:提取子字符串、反转字符串、提取文件名等
- 列表操作:获取部分元素、反转列表、分块处理等
- 数据处理:提取特定范围的数据、分页等
- 算法实现:回文检查、字符串匹配等
12.3 注意事项
- 字符串和元组不支持切片赋值(它们是不可变的)
- 列表的切片是浅拷贝,嵌套结构需要注意
- 使用负步长时,
start必须大于stop - 切片不会因为索引超出范围而报错
- 结束索引是不包含的,要记住这一点
12.4 学习建议
- 多练习:通过实际代码练习加深理解
- 画图理解:对于复杂的切片,可以画出索引图帮助理解
- 逐步调试:使用
print()输出中间结果,观察切片的效果 - 查阅文档:遇到问题时查阅 Python 官方文档
13. 附录:快速参考表
13.1 切片语法速查表
| 语法 | 说明 | 示例 |
|---|---|---|
[start:stop] |
从 start 到 stop-1 | [2:5] → 索引 2, 3, 4 |
[:stop] |
从开头到 stop-1 | [:5] → 前 5 个元素 |
[start:] |
从 start 到末尾 | [5:] → 从索引 5 到末尾 |
[:] |
整个序列的副本 | [:] → 完整副本 |
[start:stop:step] |
带步长的切片 | [::2] → 每隔一个元素 |
[::-1] |
反转序列 | [::-1] → 反转 |
[-n:] |
后 n 个元素 | [-3:] → 最后 3 个 |
[:-n] |
除了后 n 个元素 | [:-3] → 除了最后 3 个 |
13.2 常见操作速查表
| 操作 | 代码 | 结果 |
|---|---|---|
| 获取前 5 个元素 | lst[:5] |
前 5 个元素 |
| 获取后 5 个元素 | lst[-5:] |
后 5 个元素 |
| 获取中间元素 | lst[1:-1] |
去掉首尾 |
| 反转序列 | lst[::-1] |
反转后的序列 |
| 偶数索引元素 | lst[::2] |
索引 0, 2, 4, … |
| 奇数索引元素 | lst[1::2] |
索引 1, 3, 5, … |
| 获取副本 | lst[:] |
浅拷贝 |
恭喜你完成了 Python 切片的学习! 切片是 Python 中非常强大和常用的功能,掌握它会让你的代码更加简洁高效。继续练习,在实际项目中应用这些知识,你会越来越熟练!