很多人刚学列表时,最先接触的是取单个元素。
比如:
nums = [10, 20, 30, 40, 50]print(nums[2])
结果是:
30
这很好理解,索引 2 就是第 3 个元素。
但真实开发里,我们并不总是只拿一个值。更多时候,我们想拿一段数据。
比如:
取前三个成绩 取最近 7 天的订单 跳过第一个表头,只处理后面的内容 把一个列表倒过来 每隔一个元素取一次
这时候,列表切片就派上用场了。
切片最大的魅力就在于,它看起来像取值,实际上是在批量取值。一行代码,能顶很多行循环。
一、什么是切片
切片,本质上就是从一个列表里,切出一段内容。
看一个最简单的例子:
nums = [10, 20, 30, 40, 50]print(nums[1:4])
输出结果:
[20, 30, 40]
这里的 1:4,可以理解成:
从索引 1 开始取 一直取到索引 4 前面为止 注意,是到 4 为止,但不包含 4
这就是切片最核心的一条规则:
前包后不包
也就是左边包含,右边不包含。
你可以把它想成数学里的区间:
[1, 4)
包含 1,不包含 4。
所以:
nums[1:4]
实际拿到的是索引 1、2、3 的元素。
二、切片的基本语法
列表切片的完整写法是:
列表[start:end:step]
它有三个部分:
start 表示开始位置end 表示结束位置step 表示步长,也就是每隔几个元素取一次
先别急着记全,先把前两个吃透最重要。
先看几个常见例子:
nums = [10, 20, 30, 40, 50, 60]
取前 3 个元素:
print(nums[0:3])
结果:
[10, 20, 30]
取中间一段:
print(nums[2:5])
结果:
[30, 40, 50]
取最后两个元素:
print(nums[4:6])
结果:
[50, 60]
如果你发现 end 总是要多写 1,别着急,这是正常的。因为右边本来就不包含。
三、为什么切片是前包后不包
很多新手刚开始最别扭的地方,就是这里。
为什么不写成前后都包含呢?
因为前包后不包有一个特别大的好处:
长度很好算。
比如:
nums[1:4]
它一共取几个元素?
直接用 4 - 1 = 3
确实拿到了 3 个元素。
再比如:
nums[2:6]
长度就是 6 - 2 = 4
实际得到的也是 4 个元素。
这会让程序设计更统一,尤其是在处理区间、分页、分组的时候,非常顺手。
你现在可能还没特别强烈的感觉,但等你后面写数据处理,就会发现这个规则很省脑子。
四、省略开始和结束位置
切片里,start 和 end 都可以省略。
1. 省略 start
如果不写开始位置,默认从头开始。
nums = [10, 20, 30, 40, 50]print(nums[:3])
结果:
[10, 20, 30]
等价于:
print(nums[0:3])
2. 省略 end
如果不写结束位置,默认切到最后。
print(nums[2:])
结果:
[30, 40, 50]
等价于:
print(nums[2:len(nums)])
3. start 和 end 都省略
如果前后都不写,表示取整个列表。
print(nums[:])
结果:
[10, 20, 30, 40, 50]
这个写法看上去像没做事,但其实很有用。它常被用来复制列表,后面我们会讲到。
五、切片和索引的区别
这个点一定要分清。
索引取出来的是单个元素。 切片取出来的是一个新列表。
看例子:
nums = [10, 20, 30, 40, 50]a = nums[2]b = nums[1:4]print(a)print(b)print(type(a))print(type(b))
输出:
30[20, 30, 40]<class 'int'><class 'list'>
所以:
nums[2] 拿到的是一个整数nums[1:4] 拿到的是一个列表
这一点非常重要。很多报错,都是因为把切片结果当成单个值用了。
六、步长 step 到底是什么
前面说过,切片完整写法是:
列表[start:end:step]
第三个参数 step,表示每隔几个元素取一次。
看例子:
nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]print(nums[0:10:2])
结果:
[0, 2, 4, 6, 8]
因为步长是 2,所以每次跨两个位置。
再看:
print(nums[1:9:3])
结果:
[1, 4, 7]
也就是从索引 1 开始,每隔 3 个取一个。
很多人看到 step 容易晕,其实你把它理解成跳着拿,就简单了。
七、最常用的几种切片写法
下面这些,几乎是日常开发里最常见的。
1. 取前 n 个元素
nums[:3]
取前 3 个。
2. 取后 n 个元素
nums[-3:]
取最后 3 个。
3. 跳过前 n 个元素
nums[3:]
跳过前 3 个,从第 4 个开始全部取走。
4. 每隔一个取一个
nums[::2]
取偶数位置上的元素。
5. 复制整个列表
nums[:]
生成一个浅拷贝的新列表。
6. 反转列表
nums[::-1]
这个非常经典。
比如:
nums = [1, 2, 3, 4, 5]print(nums[::-1])
结果:
[5, 4, 3, 2, 1]
为什么能反转?
因为步长是 -1,表示倒着走。
八、负数索引和切片
你前面已经学过,列表支持负数索引。
-1 表示最后一个-2 表示倒数第二个
切片当然也支持。
比如:
nums = [10, 20, 30, 40, 50, 60]print(nums[-4:-1])
结果:
[30, 40, 50]
解释一下:
-4 对应 30-1 对应 60,但右边不包含,所以只取到 50
再看一个特别常见的例子:
print(nums[-3:])
结果:
[40, 50, 60]
这在处理最后几条记录时非常实用。
比如最近 3 天数据、最后 5 条日志、最新 10 条评论,基本都能这样写。
九、切片不会修改原列表
这是初学者很容易忽略的地方。
切片取出来的是一个新列表,不会直接改原数据。
看代码:
nums = [10, 20, 30, 40, 50]part = nums[1:4]print(part)print(nums)
输出:
[20, 30, 40][10, 20, 30, 40, 50]
原列表没变。
这说明,切片更像是复制出一段内容,而不是把原来的那一段剪下来。
你可以把它理解成:
索引是看某一个人 切片是拍一组人的合照
照片拍完,原来的人还在原地。
十、切片复制列表,到底是不是独立的
先看代码:
nums = [10, 20, 30]copy_nums = nums[:]copy_nums[0] = 999print(nums)print(copy_nums)
结果:
[10, 20, 30][999, 20, 30]
说明通过 [:] 得到的是一个新的列表对象。
这比直接赋值更安全。
直接赋值是这样:
nums = [10, 20, 30]copy_nums = numscopy_nums[0] = 999print(nums)print(copy_nums)
结果:
[999, 20, 30][999, 20, 30]
这时候两个变量其实指向同一个列表,所以改一个,另一个也跟着变。
因此:
想复制列表,用切片 [:] 很常见。
不过这里先提一句,[:] 是浅拷贝。如果列表里还有嵌套列表,情况会更复杂。这个阶段先记住表层复制就够用了。
十一、切片在实际开发里到底有什么用
如果只讲语法,很容易学完就忘。下面我们结合真实场景看。
1. 处理分页数据
比如一页显示 10 条数据:
users = ['用户1', '用户2', '用户3', '用户4', '用户5', '用户6', '用户7', '用户8', '用户9', '用户10', '用户11', '用户12']page1 = users[0:10]page2 = users[10:20]print(page1)print(page2)
第一页取前 10 条,第二页取第 11 到第 20 条。
以后你做后台管理、评论列表、商品展示,这个思路会非常常见。
2. 取最近一段数据
sales = [120, 132, 98, 145, 167, 180, 176]last_3_days = sales[-3:]print(last_3_days)
结果:
[167, 180, 176]
比如最近 3 天销量、最近 5 次成绩、最新 10 条消息,切片都很好用。
3. 跳过表头
很多文件读取后,第一行是表头,真正要处理的是后面的数据。
rows = ['姓名,年龄,城市', '张三,18,北京', '李四,20,上海', '王五,19,深圳']data_rows = rows[1:]print(data_rows)
结果:
['张三,18,北京', '李四,20,上海', '王五,19,深圳']
4. 取奇数位或偶数位数据
nums = [1, 2, 3, 4, 5, 6, 7, 8]print(nums[::2])print(nums[1::2])
结果:
[1, 3, 5, 7][2, 4, 6, 8]
这种写法在抽样、分类、拆分数据时很方便。
十二、切片最容易犯的几个错
1. 以为右边也会取到
很多人写:
nums = [10, 20, 30, 40, 50]print(nums[1:3])
以为结果是 [20, 30, 40]
其实正确结果是:
[20, 30]
因为 3 不包含。
2. 把切片结果当成单个元素
nums = [10, 20, 30, 40]x = nums[1:2]print(x)
结果是:
[20]
不是 20
虽然只有一个元素,但它依然是列表。
3. 步长写成 0
nums = [1, 2, 3, 4]print(nums[::0])
这会直接报错。
因为步长不能为 0,不然程序不知道怎么走。
4. start 和 end 方向不对
nums = [1, 2, 3, 4, 5]print(nums[4:1])
结果:
[]
为什么是空列表?
因为默认步长是正数,表示从左往右走。 你让它从 4 走到 1,它走不过去,所以结果为空。
如果你想反着取,要配合负步长:
print(nums[4:1:-1])
结果:
[5, 4, 3]
十三、把切片真正讲透的一个例子
来看一个稍微完整一点的案例。
现在有一周的气温数据:
temps = [23, 24, 26, 25, 22, 21, 20]
我们想做几件事:
取前 3 天气温 取后 2 天气温 每隔一天取一次 倒序查看
代码可以这样写:
temps = [23, 24, 26, 25, 22, 21, 20]first_3 = temps[:3]last_2 = temps[-2:]every_other = temps[::2]reversed_temps = temps[::-1]print('前3天气温:', first_3)print('后2天气温:', last_2)print('隔一天取一次:', every_other)print('倒序查看:', reversed_temps)
输出:
前3天气温: [23, 24, 26]后2天气温: [21, 20]隔一天取一次: [23, 26, 22, 20]倒序查看: [20, 21, 22, 25, 26, 24, 23]
你会发现,切片并不是一个零散知识点,它几乎是在用最短的方式表达数据处理逻辑。
这就是 Python 让人上头的地方。
十四、切片和 for 循环怎么选
很多同学学到这里会问:
既然切片这么方便,那是不是以后都不用循环了?
不是。
切片适合做的是:
取一段数据 复制一段数据 快速反转 按固定间隔抽取
而 for 循环更适合:
逐个处理元素 做判断 做计算 做复杂逻辑
比如你只想拿前 5 个元素,切片非常好。
nums[:5]
但如果你想把前 5 个元素都乘以 2,再判断哪些大于 10,那就更适合循环。
所以,切片是快速拿数据,循环是逐个处理数据。两者不是替代关系,而是配合关系。
十五、练习题:别光看,自己敲一遍
下面这几个练习很有代表性,建议你自己先写。
练习 1有列表:
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9]
取出前 4 个元素。
答案:
print(nums[:4])
练习 2取出后 3 个元素。
答案:
print(nums[-3:])
练习 3每隔一个元素取一次。
答案:
print(nums[::2])
练习 4把列表倒过来。
答案:
print(nums[::-1])
练习 5取出中间的 3, 4, 5, 6
答案:
print(nums[2:6])
你会发现,切片真正难的不是写法,而是脑子里能不能快速对应出索引范围。
这个能力没有捷径,就是多写几次。
十六、本章小结
这一章你只要真正记住下面这几件事,就已经很够用了。
列表切片的基本写法是:
列表[start:end:step]
最核心的规则是:
前包后不包
省略开始,默认从头开始。 省略结束,默认到最后。 省略步长,默认是 1。
常见高频写法:
nums[:3] # 前3个nums[2:] # 从索引2到最后nums[-3:] # 最后3个nums[::2] # 每隔一个取一个nums[::-1] # 倒序nums[:] # 复制整个列表
切片返回的是新列表,不会直接修改原列表。
学会切片之后,你处理列表的速度会快很多,代码也会明显更简洁。很多过去要靠循环慢慢写的操作,现在一行就够。
下一章我们继续讲 元组 tuple:为什么不可变也很重要。 你会发现,Python 里的容器不只是会装数据,它们背后其实各有适合的场景。