一、切片是什么(取一段)
切片(slice)就是从一个序列里「切下一段」。比如有一张歌单,你只想拿前三首,不用一个个写,用切片一句话搞定:
songs = ['夜曲', '晴天', '稻香', '七里香', '告白气球']
first_three = songs[0:3]
print(first_three)
输出:
['夜曲', '晴天', '稻香']
切片不只用于列表,字符串、元组一样能用。它最大的好处是:不用循环、不用数下标,一行就把「第几个到第几个」取出来。
| 你熟悉的写法 | 切片写法 |
|---|---|
| 一个个赋值取前三首 | songs[0:3] |
| 循环打印字符串前 5 个字符 | word[:5] |
| 把列表倒过来 | lst[::-1] |
课程思政|数据安全与法治素养 · 取数据,只取该取的那一段
切片的妙处,是让你「只要前面一小段」的时候,不必把整个序列都端出来。这个思路在真实工作里同样成立:做报表常常只需要某几列、某几天,其余的姓名、身份证号本就不该被带走。够用就好,能少取就少取。最小必要不只是合规上的要求,也是替别人守住隐私的习惯。
二、三步写法 [起点:终点:步长](含头不含尾)
切片用方括号,里面最多写三项,用冒号隔开:
序列[起点:终点:步长]
三项分别是什么
| 位置 | 名字 | 默认值 | 含义 |
|---|---|---|---|
| 第 1 项 | 起点 | 0(开头) | 从哪个下标开始取(包含) |
| 第 2 项 | 终点 | 末尾 | 取到哪个下标之前(不包含) |
| 第 3 项 | 步长 | 1 | 每次跳几格;正数往前、负数往后 |
最重要的规则:含头不含尾
songs[0:3] 表示从下标 0 开始,取到下标 3 之前——也就是取下标 0、1、2,不取 3。
scores = [60, 75, 82, 90, 100]
print(scores[1:4])
输出:
[75, 82, 90]
下标 1 是 75、2 是 82、3 是 90,但终点 4 不含,所以取到 90 就停。取下标 1、2、3 三个元素。
[:3],取前 5 个写 [:5]。[a:b] 心里默念「从 a 取到 b 前面一个」。三、四种最常用写法
把三项省略成几种最常用的组合,记住这四种就够用了:
| 写法 | 含义 | 例子(对 [0,1,2,3,4,5]) |
|---|---|---|
[:] | 从头到尾全部 | 得到 [0,1,2,3,4,5] |
[a:b] | 从 a 取到 b 前 | [1:4] → [1,2,3] |
[:b] | 从头取到 b 前 | [:3] → [0,1,2] |
[a:] | 从 a 取到末尾 | [3:] → [3,4,5] |
挨个跑一遍
lst = [0, 1, 2, 3, 4, 5]
print(lst[:])
print(lst[1:4])
print(lst[:3])
print(lst[3:])
输出:
[0, 1, 2, 3, 4, 5]
[1, 2, 3]
[0, 1, 2]
[3, 4, 5]
[:] 常用来复制一整个列表(拿到的是新的一份)。四、负数下标
下标除了从 0 从左往右数,还能用负数从右往左数:-1 是最后一个,-2 是倒数第二个……
pets = ['旺财', '咪咪', '豆豆', '团子', '雪球']
print(pets[-1])
print(pets[-2:])
输出:
雪球
['团子', '雪球']
pets[-2:] 表示从倒数第二个开始,取到末尾——正好是最后两个。
| 写法 | 取到什么 |
|---|---|
[-1] | 最后一个元素 |
[-3:] | 最后三个元素 |
[:-1] | 除最后一个以外的全部 |
[-3:-1] | 倒数第三到倒数第二(不含最后一个) |
s = 'abcdef'
print(s[-4:-1])
输出:
cde
五、步长([::2]、[::-1] 反转)
第三项是步长:每隔几格取一个。写成 序列[起点:终点:步长]。
① 每隔一个取(步长 2)
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(nums[::2])
输出:
[1, 3, 5, 7, 9]
② 反转序列(步长 -1)
queue = ['小满', '阿离', '大可', '团子', '咕噜']
print(queue[::-1])
输出:
['咕噜', '团子', '大可', '阿离', '小满']
③ 倒着每隔一个取(步长 -2)
levels = ('青铜', '白银', '黄金', '铂金', '钻石', '星耀', '王者')
print(levels[::-2])
输出:
('王者', '钻石', '黄金', '青铜')
| 步长 | 效果 |
|---|---|
2 | 每隔一个取,正着走 |
3 | 每三个取一个 |
-1 | 整个反转 |
-2 | 反转后每隔一个取 |
-1,别写成 0。[::-1] 这个写法:它是 Python 里反转序列最常用的一招,字符串、列表、元组通吃。AI 助手小贴士|负步长是切片最难啃的一口,让 AI 一步步推导。
打开任意 AI 对话工具(网页版或 App 都可以),试试这样问:s[3:1:-1] 会取到哪些字符?请按 step 为负时 start 和 stop 的含义一步步推导,再出 2 道反向切片题考我
课程思政|工匠精神与严谨求实 · 方向写反,越取越空
步长的正负决定了走的方向:用负数步长时,起点必须比终点大,否则一个元素都取不到,只能对着空结果发呆。这类参数最怕凭印象写,多想一步「从哪往哪走、到哪停」,往往比改十次代码都省事。先想清楚规则,再动手敲,这份耐心正是严谨求实的底色。
六、超出范围不报错(和索引越界对比)
切片一个很友好的特点是:下标超出范围也不会报错,它会自动截到序列末尾。
lst = [1, 2, 3]
print(lst[1:10]) # 终点 10 远超长度,只取到末尾
输出:
[2, 3]
这和「单个下标」完全不同
| 写法 | 结果 |
|---|---|
lst[1:10](切片) | 不报错,返回 [2, 3] |
lst[5](单个下标) | 报错 IndexError |
单个下标访问越界会崩,但切片越界只是「能拿多少拿多少」。这是因为切片本来就是为了「安全地取一段」。
lst = [1, 2, 3]
print(lst[5]) # 下标 5 不存在
IndexError: list index out of range
data[:100] 即使不足 100 个也不会错。七、字符串/列表/元组都通用(返回同类型)
切片的写法在字符串、列表、元组上完全一样,而且返回的类型和原来一致:字符串切片还是字符串,元组切片还是元组。
# 字符串切片,结果仍是字符串
word = 'strawberry'
print(word[:3])
输出:
str
元组的例子(返回还是元组)
week = ('周一', '周二', '周三', '周四', '周五', '周六', '周日')
print(week[2:5])
输出:
('周三', '周四', '周五')
字符串取中间一段
slogan = '今天也要元气满满'
print(slogan[4:8])
输出:
元气满满
| 原类型 | 切片结果类型 | 例子 |
|---|---|---|
| 列表 | 列表 | lst[1:3] → 列表 |
| 字符串 | 字符串 | s[1:3] → 字符串 |
| 元组 | 元组 | t[1:3] → 元组 |
八、切片不改原对象,重新赋值才改(lst[1:3] = [...])
用切片读取一段,原来的序列不会变,得到的是一份新的:
a = [1, 2, 3]
b = a[1:3] # 切出一段
b[0] = 99 # 改 b
print('a =', a)
print('b =', b)
输出:
a = [1, 2, 3]
b = [99, 3]
改了 b,a 纹丝不动。说明切片返回的是新的副本。
想改原列表,要「重新赋值」
lst = [1, 2, 3, 4, 5]
lst[1:3] = [9, 9, 9] # 用切片当「位置」做替换
print(lst)
输出:
[1, 9, 9, 9, 4, 5]
这里把下标 1、2 两个位置,换成了三个新元素,所以列表反而变长了。注意:这是「赋值给切片」,和「读切片」是两回事。
b = a[1:3] 是「读」,原列表不变;a[1:3] = [...] 是「写」,会改原列表。少写一个等号,意思差很多。new = old[:]。比起 new = old(两个变量指向同一个列表),[:] 才是一份真正独立的副本。课程思政|诚信与职业操守 · 副本上改,底稿要干净
b = a[1:3] 是「读」,原列表纹丝不动;a[1:3] = ... 是「写」,才会改到原表。少一个等号,意思差很多。处理真实数据也该这样:分析和修改都在副本上做,原始底稿保持原样,谁改过、改了什么都留得下痕迹。底稿干净,结论才站得住——这是技术习惯,也是做事的诚信。
九、实战:读一张身份证号(索引+切片+正负下标)
身份证号是一个 18 位的定长字符串,每一位都有固定含义。它是练索引和切片最合适的例子:长度固定、每一位都不多余,取哪一段、从哪边数,一验就知道对不对。下面先把「第几位」和「下标是多少」对上号。
先看这张位序表
从左往右数用正索引(从 0 开始),从右往左数用负索引(从 -1 开始):
| 身份证号 | 4 | 4 | 0 | 5 | 8 | 3 | 1 | 9 | 8 | 7 | 0 | 4 | 2 | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 正索引 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
| 负索引 | -18 | -17 | -16 | -15 | -14 | -13 | -12 | -11 | -10 | -9 | -8 | -7 | -6 | -5 | -4 | -3 | -2 | -1 |
出生年 · 索引 6~9出生月 · 索引 10~11出生日 · 索引 12~13后四位 · 索引 14~17
三个最容易记错的点:正索引从 0 开始(第 1 位对应下标 0);负索引从 -1 开始(最后一位是 -1);「第 N 位」永远比下标大 1——比如第 17 位的下标是 16。
18 位分成四段,每段管一件事
| 位次 | 长度 | 切片写法 | 含义 |
|---|---|---|---|
| 第 1~6 位 | 6 | id_no[:6] | 地址码(省、市、县) |
| 第 7~14 位 | 8 | id_no[6:14] | 出生年月日,8 位数字 |
| 第 15~17 位 | 3 | id_no[14:17] | 顺序码,其中第 17 位表示性别 |
| 第 18 位 | 1 | id_no[-1] | 校验码,可能是 0~9 或 X |
下面用示例号码 440583198704201234 演示,它是虚构的:44 开头代表广东省、4405 是汕头市,再往后的区县编码和生日都只是凑数,不必当真。
取出生年月日、后四位、地址码
三段要的信息,三行切片就能取出来:
id_no = '440583198704201234'
print(id_no[0:6]) # 前六位:地址码
print(id_no[6:14]) # 出生年月日,8 位
print(id_no[-4:]) # 后四位(含校验码)
输出:
440583
19870420
1234
回忆一下「含头不含尾」:[6:14] 取的是下标 6 到 13,正好 8 个字符。要是取出来少一位或多一位,先回上面那张位序表数一遍下标。
正索引和负索引,取的是同一段
id_no = '440583198704201234'
print(id_no[6:14], id_no[-12:-4])
print(id_no[14:], id_no[-4:])
print(id_no[16], id_no[-2])
输出:
19870420 19870420
1234 1234
3 3
三组结果一一对应。负索引的好处是不用知道总长度——取后四位就写 [-4:],比先数到 18 再写 [14:] 省心,号码长一点短一点都照样管用。而「从第 7 位取到第 14 位」这种落在中间的一段,正索引更直观:[6:14] 一眼看清从哪儿到哪儿。
判断性别:看第 17 位的奇偶
第 17 位(也就是下标 16,或者从右数的 [-2])是性别位:奇数表示男,偶数表示女。把这个字符取出来转成整数,再判断奇偶:
id_no = '440583198704201234'
gender = int(id_no[16]) # 第 17 位,等价写法 id_no[-2]
print('男' if gender % 2 else '女')
输出:
男
gender % 2 余 1 为真、余 0 为假,正合「奇男偶女」,所以一个三元表达式就够了。换一个倒数第二位是偶数的号码试试:
for id_no in ['440583198704201234', '440583198704201246']:
code = int(id_no[16])
print(code, '→', '男' if code % 2 else '女')
输出:
3 → 男
4 → 女
最后一位可能是 X,它代表 10
第 18 位是校验码,由前 17 位按固定权重算出来,结果可能落在 0~9,也可能是字母 X。为什么会有 X?因为算出来的值有时正好是 10,一位放不下「10」,就用罗马数字 X 顶上。所以最后一位不能想当然地当数字用——直接转整数,遇到 X 当场就崩:
id_no = '44058319870420123X'
print(int(id_no[-1]))
ValueError: invalid literal for int() with base 10: 'X'
办法是先看是不是 X:是就按 10 处理,不是再转整数。
id_no = '44058319870420123X'
last = id_no[-1].upper() # 统一成大写,输入不规范时也认
value = 10 if last == 'X' else int(last)
print(last, '代表', value)
输出:
X 代表 10
还有个细节:取性别位不会遇到这个问题——第 17 位一定是数字,会出 X 的只有最后一位。
连起来:一次读出全部信息
把上面几段拼一起,一条一条打印,正好对应四段结构:
id_no = '440583198704201234'
print('地址码:', id_no[:6])
print('出生日期:', f'{id_no[6:10]}-{id_no[10:12]}-{id_no[12:14]}')
print('顺序码:', id_no[14:17])
print('性别:', '男' if int(id_no[16]) % 2 else '女')
print('校验码:', id_no[-1])
输出:
地址码: 440583
出生日期: 1987-04-20
顺序码: 123
性别: 男
校验码: 4
两个提醒:一是示例号码是虚构的,只用来演示位置;真实号码属于个人敏感信息,写练习一律用编的号码,别拿别人的顶替。二是第 18 位的校验码由前 17 位按固定权重算出,可能是 0~9 也可能是 X——示例里的末位只是占位,不用去对算法。
十、十个基础案例(对应课堂 10 个例子)
下面 10 个案例都来自课堂讲解的切片示例,每个都真跑过。注意第 2 个例子里那个前导空格。
案例 1:取列表前三个
animations = ['火影忍者', '海贼王', '名侦探柯南', '灌篮高手', '龙珠']
print(animations[0:3])
输出:
['火影忍者', '海贼王', '名侦探柯南']
案例 2:取句子的后半部分
sentence = 'good afternoon my friend'
print(sentence[14:])
输出:
my friend
注意:输出开头有一个空格!因为下标 14 正好落在 'afternoon' 后面的那个空格上。示例里常漏写这个空格,记住它是真实存在的。
案例 3:取元组中间几个
week = ('周一', '周二', '周三', '周四', '周五', '周六', '周日')
print(week[2:5])
输出:
('周三', '周四', '周五')
案例 4:反转列表
queue = ['小满', '阿离', '大可', '团子', '咕噜']
print(queue[::-1])
输出:
['咕噜', '团子', '大可', '阿离', '小满']
案例 5:每隔一个取
ranks = ['青铜', '白银', '黄金', '铂金', '钻石', '星耀', '王者']
print(ranks[::2])
输出:
['青铜', '黄金', '钻石', '王者']
案例 6:取最后两个
menu = ['螺蛳粉', '麻辣烫', '烧烤', '炸鸡', '奶茶']
print(menu[-2:])
输出:
['炸鸡', '奶茶']
案例 7:截取单词前三个字符
word = 'strawberry'
print(word[:3])
输出:
str
案例 8:步长 -2 反转取
levels = ('青铜', '白银', '黄金', '铂金', '钻石', '星耀', '王者')
print(levels[::-2])
输出:
('王者', '钻石', '黄金', '青铜')
案例 9:从中间到末尾每隔一个
temps = [18, 20, 22, 24, 26, 28, 30, 32, 34]
print(temps[4::2])
输出:
[26, 30, 34]
案例 10:截取长字符串中间一段
lyric = '我想去看海边的日出和日落'
print(lyric[4:9])
输出:
海边的日出
AI 助手小贴士|切片要练到「看结果写表达式」的条件反射。
打开任意 AI 对话工具(网页版或 App 都可以),试试这样问:出 5 道写切片的题:给我字符串和想要的结果(比如从「Python」里取出「tho」),让我写切片表达式,写完对答案
十一、常见坑(步长 0、负数步长方向、浅复制)
坑 1:步长写成 0
nums = [1, 2, 3]
print(nums[::0])
ValueError: slice step cannot be zero
步长不能是 0(每跳 0 格永远停在原地,没意义)。想反转用 -1。
坑 2:负数步长的方向搞反
用负数步长时,起点要比终点大,否则什么都取不到:
s = 'hello'
print(s[3:0:-1]) # 从 3 往回走到 1
输出:
lle
s[3:0:-1]:从下标 3 开始,往回走(步长 -1),到下标 0 之前停,所以取 3、2、1,得到 'lle'。如果写成 s[0:3:-1] 反而取不到东西,因为起点 0 比终点 3 小、却又用负步长往回走,矛盾了。
坑 3:以为切片改了原对象
a = [1, 2, 3]
b = a[:]
b[0] = 99
print(a) # a 没变
输出:
[1, 2, 3]
用 a[:] 复制后改副本,原列表不受影响。只有 a[1:3] = ...(把切片写在等号左边)才会改原列表。
坑 4:切片是「浅」复制
a = [[1, 2], [3, 4]]
b = a[:] # 复制外层列表
b[0][0] = 99 # 改内层列表的元素
print(a)
输出:
[[99, 2], [3, 4]]
切片只复制了外层,里面嵌套的列表还是同一份。改了 b[0] 里的内容,a 也跟着变。嵌套结构要小心这点。
坑 5:「第 N 位」不是「下标 N」
身份证号第 17 位是性别位,很多人顺手就写 id_no[17]——拿到的其实是第 18 位。这一类错误不会报错,只是结果悄悄错了,最难查:
id_no = '440583198704201234'
print('第 17 位:', id_no[17]) # 错:这是第 18 位
print('第 17 位:', id_no[16]) # 对
输出:
第 17 位: 4
第 17 位: 3
两个数字都合法,程序照跑不误,但性别判反了。口诀记牢:下标 = 第几位 − 1;拿不准就回头看位序表。
lst[1:3](读,返回新列表)和 lst[1:3] = [...](写,改原列表)。写代码时盯一下等号在不在切片左边。十二、报错手册
切片相关的报错不多,下面都是真跑出来的原文。
① 步长为 0
nums = [1, 2, 3]
print(nums[::0])
ValueError: slice step cannot be zero
原因:步长不能是 0。想反转用 -1,想隔一个用 2。
② 单个下标越界(注意:切片越界不报这个)
lst = [1, 2, 3]
print(lst[5])
IndexError: list index out of range
原因:用单个下标访问不存在的位置会崩。如果只想「尽量取一段」,用切片 lst[3:100] 就不会错。
③ 对象不支持切片
n = 10
print(n[1:3])
TypeError: 'int' object is not subscriptable
原因:只有序列(字符串/列表/元组等)才能切片,单个数字 10 不行。
④ 下标或步长不是整数
lst = [1, 2, 3]
print(lst[1:3.0])
TypeError: slice indices must be integers or None or have an __index__ method
原因:切片的起点、终点、步长都必须是整数,3.0 这种浮点数不行(哪怕它的值等于 3)。
⑤ 校验码是 X,转整数就崩
id_no = '44058319870420123X'
print(int(id_no[-1]))
ValueError: invalid literal for int() with base 10: 'X'
原因:int() 只认数字字符,'X' 不是。身份证号最后一位可能是 X(代表校验值 10),碰到它就先用 if 拦一下:是 X 就当 10 处理,否则再转整数。
AI 助手小贴士|负索引和步长是切片最容易晕的地方。让 AI 从三个参数的含义一步步推导,比死记结论扎实。
打开任意 AI 对话工具(网页版或 App 都可以),试试这样问:s[::-1] 为什么能反转字符串?请从切片 start、stop、step 三个参数的含义讲起,再出 3 道切片题考我(要包含负索引和步长)
十三、随堂自测(12 题)
点击选项即可判分,选错的题会立刻展开解析。做完可以看到正确率和错题清单,也可以重新答题。
1[1,2,3,4,5][1:4] 取到的是哪些下标对应的元素?
- A. 下标 0、1、2
- B. 下标 1、2、3
- C. 下标 1、2、3、4
- D. 下标 2、3、4
[2,3,4]。2[10,20,30,40,50][2:5] 的结果是什么?
- A.
[20,30] - B.
[20,30,40,50] - C.
[30,40,50] - D.
[40,50]
[30,40,50]。3lst[:] 表示取什么?
- A. 从头到尾整个序列
- B. 一个空序列
- C. 只有最后一个元素
- D. 会报错
lst[:] 常用来复制整个列表,得到一份独立的新副本。4nums[::2] 里的步长 2 是什么意思?
- A. 只取前两个元素
- B. 倒着取一遍
- C. 每隔一个取一个
- D. 只取第一个元素
[1,2,3,4,5] 会得到 [1,3,5]。想反转要写负步长 -1。5想把列表 lst 反转,最常用的是?
- A.
lst[::2] - B.
lst[::-1] - C.
lst[-1::] - D.
lst[:0:-1]
[::-1] 是 Python 反转序列的标准写法:步长 -1 从尾走到头,字符串、列表、元组都能用。6想取列表 lst 的最后两个元素,正确的是?
- A.
lst[:2] - B.
lst[-2::] - C.
lst[:-2] - D.
lst[-2:]
lst[-2:] 表示从倒数第二个取到末尾,正好是两个;[:-2] 反而是「除最后两个外全部」。7切片时终点下标超出了序列长度,会发生什么?
- A. 不报错,自动截到末尾
- B. 报 IndexError
- C. 返回 None
- D. 只取到倒数第二个
lst[5])才会报 IndexError。8用单个下标访问 lst[5],但列表只有 3 个元素,会怎样?
- A. 返回 None
- B. 返回最后一个元素
- C. 不报错返回空
- D. 报 IndexError
IndexError: list index out of range;想安全取一段要用切片 lst[3:100]。9写 nums[::0](步长为 0)会怎样?
- A. 返回一个空列表
- B. 报 ValueError: slice step cannot be zero
- C. 返回原序列
- D. 报 SyntaxError
ValueError。反转请写 -1,隔一个取写 2。10用切片读取 b = a[1:3] 之后,再改 b,原列表 a 会怎样?
- A. 也会被改
- B. 报错
- C. 不会被改,返回的是新对象
- D. 两个都变成空
b 不影响 a。只有把切片写在等号左边(a[1:3] = ...)才会改原列表。11身份证号 id_no = '440583198704201234',想取出出生年月日(19870420),下面哪个写法对?
- A.
id_no[6:14] - B.
id_no[7:15] - C.
id_no[6:13] - D.
id_no[-12:]
id_no[6:14]。[7:15] 整体后移一位,[6:13] 少取一位,[-12:] 等于 [6:],会把后四位也带上(得到 12 个字符)。12某个身份证号最后一位是 X,执行 int(id_no[-1]) 会怎样?
- A. 返回 0
- B. 返回 10
- C. 返回 None
- D. 报
ValueError: invalid literal for int() with base 10: 'X'
int() 只接受数字字符,'X' 会被判定为非法字面量并抛异常。X 代表校验值 10,正确做法是先判断:10 if id_no[-1].upper() == 'X' else int(id_no[-1])。