每个知识点先给生活类比,再落到代码,让你看得到"为什么"。
每个知识点后紧跟在线可运行习题,写完立刻按 Ctrl + Enter 运行。
穿插即时选择题与综合案例,最后一题是把整章串起来的小项目。
变量爆炸、无法循环、改一个名字要改 50 处。
一个名字管住全部数据,编号、截取、排序全都有现成工具。
序列 / 容器
列表 · 元组 · 字符串
可变 vs 不可变
索引 · 切片 · 相加
乘法 · 成员资格
len / min / max
list() · 改元素 · del
切片赋值
11 个常用方法
不可修改的序列
逗号的魔力
列表与元组对照
准确说出序列、索引、切片的含义,解释为什么索引从 0 开始、为什么切片"顾头不顾尾"。
熟练使用 [i]、[a:b]、[a:b:step]、+、*、in 处理列表、元组与字符串。
会用 append / insert / pop / remove / sort / sorted / index 等方法管理一个真实列表。
知道 y = x.sort() 为什么得到 None,知道 b = a 与 b = a.copy() 的天壤之别。
清楚列表与元组的区别,能用 tuple 表达"不允许修改"的数据(如坐标、RGB 颜色)。
独立完成"班级成绩管理"小项目:录入、查分、排名、统计、榜单输出。
在 Python 里,最基本的数据结构是序列。序列中的每个元素都有编号——这个编号,我们叫它"索引"。
以某种方式(如编号)组合起来的数据元素集合。元素可以是数、字符,甚至是另一个数据结构。
Python 中最基本的数据结构。每个元素都带编号:第一个元素索引为 0,第二个为 1,依此类推。
可包含其他对象的对象。两大类:序列(元素有编号,如列表)与映射(元素有名字/键,如字典,第 4 章)。集合 set 是第三类。
一列为一组,row[0] 是第一排。座位不动,人可换 → 列表。
周一到周五是 5 个元素,timetable[2] 是周三的课。
字符串也是序列!idcard[6:14] 直接切出出生日期。
方括号 [ ]。可变 —— 随时增删改。
本章主角,Python 的"主力"。
圆括号 ( )。不可变 —— 一旦创建不能改。
适合放"不许改"的数据。
引号。不可变 —— 字符的序列。
第 3 章详细讲,本章操作对它全适用。
可以随时插入新页、撕掉旧页、涂改某一页。适用于中途需要添加元素的场景。
印出来就定型了,改不动。适用于出于某种考虑必须禁止修改的场景。
① 元组可作字典的键,列表不行(第 4 章);
② 有些内置函数/方法返回的就是元组,你躲不开;
③ 表达"这组值不该被改"的设计意图。
自己写程序时,几乎总能用列表代替元组。唯一硬限制:当元组被当作字典键或集合成员时,不能换。
len() 数出元素个数;ming[-1] 直接取最后一个。database[1][0]:先取出第 2 个人,再取他的姓名。group = ['赵一','钱二','孙三','周五'],group[2] 的结果是?guess = 0 让程序能跑起来(会输出"赵一"),再把它改成 -1。
第 4 步改的是列表自身,不需要重新赋值——这正是"可变"的含义。索引、切片、相加、相乘、成员资格检查 —— 这五件事对列表、元组、字符串全都通用。学会一次,三处受益。
| 操作 | 写法 | 含义 | 生活类比 |
|---|---|---|---|
| 索引 | seq[i] | 取第 i+1 个元素 | 报学号,叫一个人起来答题 |
| 切片 | seq[a:b] | 取 a 到 b-1 这一段 | 点名"第 2 排到第 5 排" |
| 相加 | seq1 + seq2 | 两个序列拼起来(同类型) | 两个班合并成一个大名单 |
| 乘法 | seq * n | 把序列重复 n 次 | 复印 30 份同样的试卷 |
| 成员资格 | x in seq | 判断 x 在不在里面 → True/False | 查名单上有没有这个人 |
len(seq) 元素个数max(seq) 最大元素min(seq) 最小元素
所有操作都围绕"编号"展开。把编号想明白,切片和步长自然就通了。
对序列逐个处理叫迭代(for 循环),将在 5.5 节详细讲。今天先手动取。
Python 没有专门的"字符类型"。greeting[0] 得到的 'H' 是字符串。
'Hello'[1] 与先赋值给变量再索引效果完全一样。
input('Year: ')[3] —— 拿到输入的第 4 位,一步到位。
seq[3] —— 那其实是第 4 个。第 3 个是 seq[2]。
索引描述的不是"第几个",而是"离开头多远"。第一个元素就在开头,距离为 0。
这样定义后,切片的长度可以直接用 b - a 算出来——这是数学上的美感。
若从 1 开始编号,seq[1:3] 里到底有 2 个还是 3 个元素?
从 0 开始 + 顾头不顾尾:元素个数 = 尾 - 头,永远成立。
C、Java、JavaScript 等主流语言都从 0 开始。早适应早受益。
"第 n 个,索引 n-1"
-5。当你不知道列表有多长,却想拿最后几个元素时。比如"最新一次月考成绩"。
长度为 n 的序列,负索引 -k 等价于正索引 n - k。
例:n=5,-2 ≡ 3。
只有 3 个人(索引 0/1/2),却去要第 4 个。Python 立刻报错,不会给你 None。
a = [10, 20, 30, 40],下面哪个写法不会报错?IndexError 先检查两件事——① 列表是不是空的;② 索引是不是比"长度-1"大了。months[month-1]:月份输 8,但 8 月存在索引 7,必须减 1endings[day-1]:同理,16 日对应索引 15endings 用加法与乘法拼出 31 个后缀:1st 2nd 3rd 4th…20th 21st…months 换成 ['一月','二月',…],删掉 endings,输出 print(month_name + str(day) + '日') —— 这就是教材里的"日期格式化"。stu[-1] 先拿到最内层列表 ['数学',88],再 [1] 拿分数。嵌套结构就是"一层一层拆"。seq[起点 : 终点]用冒号分隔两个索引。结果是同类型的新序列:列表切片还是列表,字符串切片还是字符串。
numbers[3:6] 是 3 个元素?因为 6 - 3 = 3。终点索引的元素不在结果里,个数刚好等于"终点 - 起点"。
tag[32:-4]:从索引 32 一直取到倒数第 4 个之前。
numbers[7:100] 只会把后面能给的都给你。这与单个索引 numbers[100] 报错形成鲜明对比。seq[a:b] 的元素个数 = b - a。
想取 5 个,就写 seq[i:i+5]。
seq[:3] + seq[3:] 恰好等于原序列,不重不漏。
seq[2:2] 是空序列,它标出了"第 2 个元素前面"这个插入点。
s = 'Hello world',s[2:7] 的结果是?| 写法 | 等价 |
|---|---|
seq[a:] | 从 a 一直到末尾 |
seq[:b] | 从开头一直到 b-1 |
seq[:] | 整个序列(复制) |
[-3:-1] 拿不到 10?因为 -1 就是 10 所在位置,而终点不包含。想包含最后一个,要么写 [-3:],要么理解:负索引做终点时同样"顾头不顾尾"。
seq[:] 的妙用这是复制列表最简洁的写法之一(另一种见 copy(),2.3.3 节)。
默认步长是 1(一个挨一个取)。步长为 n 表示"每隔 n-1 个取一个"。
全班 40 人按学号站好,"每隔 4 个抽 1 个"点名 → 步长 4,抽中学号 1、5、9…
numbers[::0] 会报 ValueError: slice step cannot be zero —— 原地不动没法前进。
seq[::2](第 1、3、5…个)、seq[1::2](第 2、4、6…个)。seq[::-1]把序列倒过来!这是本章最值得背下来的一行代码。
s == s[::-1] —— 一句话判断"上海海上""level"是不是回文串。
[::-1] 能反转?起点和终点都省略时,Python 会按步长方向自动决定:正步长从头走到尾,负步长从尾走到头。'http://www.' 共 11 个字符 → 域名从索引 11 开始'.org' 共 4 个字符 → 终点用 -4,正好停在 g 之前它假设网址一定是 http://www.xxx.com 这种格式。
遇到 https:// 或 .cn 就失效了。
这不怪你,书上的例子只是演示切片。
用 split('.') 或 find('//') 定位,就不再依赖写死的数字了。
https://www.baidu.com,url[11:-4] 会切出什么?(答案:'ww.baidu' —— 所以死数字不可靠)| 写法 | 结果(以 s='Python' 为例) |
|---|---|
s[0] | 'P' 取 1 个 |
s[1:4] | 'yth' 取索引 1~3 |
s[:2] | 'Py' 从头到索引 1 |
s[4:] | 'on' 从索引 4 到末尾 |
s[-1] | 'n' 最后一个 |
s[-3:] | 'hon' 最后三个 |
| 写法 | 结果 |
|---|---|
s[:] | 'Python' 完整复制 |
s[::2] | 'Pto' 隔一个取一个 |
s[::-1] | 'nohtyP' 反转 |
s[::-2] | 'nhy' 倒着隔一个取 |
s[10:20] | '' 越界也不报错 |
s[2:2] | '' 空切片=插入点 |
seq[起点 : 终点 : 步长],含头不含尾,步长控制方向与间隔;三者都可省略。s[-3:-1] 只有 2 个元素,不是 3 个;想要"最后 3 个"请写 s[-3:]。sc[1:4] 有几个元素?'信息技术' 各占 1 个索引(中文一个字一个字符),所以 s[4:] 正好是 'Python'。虽然都是序列,但列表 + 字符串 不被允许。一般规则:只能拼接同类型的序列。
1 班名单 + 2 班名单 = 年级名单(同类型可拼)。
名单 + 一个分数 = ❌ 拼不起来(类型不同)。
[1,2,3] + list('abc') 或 str([1,2,3]) + 'abc'(list() 见 2.3.1 节)。教材里常见的 print('-' * 40) 就是字符串乘法的典型场景。
None 表示"什么都没有"。要预留 10 个位置但暂时没内容,就用 [None]*10。
[0]*10 创建的是 10 个 0;[None]*10 创建的是 10 个"空位"。语义不同:0 是一个值,None 表示没有值。text_width = len(sentence) 量出句子长度box_width = text_width + 6 左右各留 3 格left_margin = (80 - box_width)//2 居中(// 是整除)把边框字符改成 '★',或用 '=',做出你自己风格的"成绩单标题框"。
'-' * 30。+ 合并两个班级的名单* 生成分隔线与占位列表None 的"占位"含义row[:] 复制——如果直接写 [row]*3,三行会是同一个列表,改一个全变(这就是后面要讲的引用陷阱)。一个布尔运算符:检查结果并返回 True(真)或 False(假)。这类值叫布尔值,将在 5.4 节详讲。
一般 in 判断"是不是其中一个元素"。但字符串的元素是字符,所以:'P' in 'Python' → True(检查字符)'yth' in 'Python' → True(还能查子串)
not in'x' not in permissions → True,表示"不在里面"。
'张伟' in 点名册。在 → True,不在 → False。垃圾邮件过滤也用这招:含 '$$$' 就判为广告。database 的每个元素是一个 [用户名, PIN] 小列表。把用户输入也拼成同样的小列表,in 就能整条比对。
↑ ['albert','1234'] in database → True
真实系统绝不会这样存密码(要加密存储)。这里只是演示 in 的用法。
else: print('Access denied'),让它拒绝错误密码(if/else 第 5 章正式学)。len(seq)返回序列元素个数。
中文一个字算 1 个字符。
min(seq)返回最小的元素。
字符串按字典序比大小。
max(seq)返回最大的元素。
大写字母比小写"小"(ASCII 码)。
max(2, 3)、min(9, 3, 2, 5) —— 不传序列,直接把数一个个丢进去也行。
max([1, 'a']) 会报 TypeError——数字和字符串没法比大小。'yth' in 'Python' 的结果是?已知全年级成绩列表,请:① 判断某人是否缺考(不在名单中);② 输出人数、最高分、最低分、平均分。
for 循环与 sorted(),属于"提前尝鲜"——正式内容在第 5 章与 2.3.3 节。列表不只是"能装东西"——它可以被修改。改元素、删元素、给切片赋值,外加 11 个趁手的方法,这才是真正的生产力。
list() 拆成列表,改完再用 join() 拼回去。''.join(列表)join 的详细用法在第 3 章(3.4.3 节)。今天先记住这句"咒语"。
不只是字符串——元组、range、甚至另一个列表都行。
list 是一个"类"(类型),不是普通函数。现阶段你把它当函数用完全没问题,第 9 章会解释差别。不需要 x = x[1]=2 这种写法,也不用重新赋值整个列表——列表自己在原地被改了。
长度只有 2,就不能改索引 100。要能改,得先让列表长到至少 101 —— 用 [None]*101 初始化。
s = 'Hello'; s[0] = 'J' 会报 TypeError。这正是列表与字符串的分水岭。删除后后面的元素全部前移,索引会重新编号。这和"赋值为 None"完全不同。
还能删字典元素(第 4 章)、删变量本身(第 5 章)。它是 Python 的一条通用语句。
del 按位置删且不给回东西;pop 按位置删并返回;remove 按值删。后面细讲。
一次给多个元素赋值。
切片可以换成长度不同的序列 → 列表会伸长/缩短。
[1:1] 是空切片 = 插入点。
给切片赋一个空列表,就是删除这一段。
代价:可读性一般,日常更推荐 insert/remove。
方法是与对象(列表、字符串等)绑定在一起的函数。调用格式:对象.方法(参数)
—— 比函数调用前面多了"对象 + 一个点"。第 7 章讲清楚它到底是什么。
| 方法 | 改列表? | 作用 |
|---|---|---|
append(x) | ✅ 就地 | 把 x 加到末尾 |
clear() | ✅ 就地 | 清空整个列表 |
copy() | ❌ | 返回列表的副本 |
count(x) | ❌ | 统计 x 出现次数 |
extend(seq) | ✅ 就地 | 把另一个序列逐个追加到末尾 |
index(x) | ❌ | 查 x 第一次出现的位置 |
| 方法 | 改列表? | 作用 |
|---|---|---|
insert(i, x) | ✅ 就地 | 在位置 i 插入 x |
pop([i]) | ✅ 就地 | 删除并返回值(默认最后一个) |
remove(x) | ✅ 就地 | 删除第一个等于 x 的元素 |
reverse() | ✅ 就地 | 反转列表元素顺序 |
sort() | ✅ 就地 | 排序(可加 key / reverse) |
另:sorted(seq) 是内置函数不是方法,返回新列表,不改原列表。 | ||
None)。写 y = x.append(1) 会得到 None——这是新手第一大坑。把 x 当作一个整体加进去。
把序列拆开一个一个加。
插到索引 i 之前。
+extend 效率更高(不用造副本);+ 不改原列表。
list(会挡住内置函数),用 scores、price_list 这种有信息量的名字。浏览器的"后退"、编辑器的 Ctrl+Z,本质都是从一个历史栈里 pop 出上一个状态。
像一叠餐盘:只能从顶部放(append)和取(pop)。
最后放上去的,最先被取走。
insert(0, x) 配 pop(),或 append() 配 pop(0)。更好的方案是 collections.deque(第 10 章)。只删第一个匹配的元素;找不到就报错;不返回值。
注意:是清空内容,列表对象还在(长度变 0)。
reverse() 就地改;reversed() 返回迭代器不改原列表,需 list() 转换。
| 写法 | 依据 | 返回值 |
|---|---|---|
del x[2] | 位置 | 无 |
x.pop(2) | 位置 | 有(被删元素) |
x.remove('be') | 值 | 无 |
x = x.remove('be') 或 x = x.reverse() —— 它们返回 None,会把你的列表变成 None!find 返回 -1)。稳妥做法:先 if x in lst: 再 index。上面 x.count(1) 得 2 —— 嵌套在 [1,2] 里的 1 不参与计数。
scores = [100, 92, 100, 88, 100] 中有几个满分?(答案:scores.count(100) → 3)
第一次满分出现在第几位?(答案:scores.index(100) → 0)sort() 就地修改 x(变成 [1,2,4,6,7,9]),但不返回值——y 拿到 None。
想留原列表又用 sort()?先 y = x.copy() 再排。注意 y = x 不行——只是同一个列表的两个名字。
sort() —— 方法,改自己,返回 None;sorted() —— 函数,不动原数据,返回新列表。(Python 2.3+ 为稳定排序)x=[3,1,2]; y=x.sort() 之后,x 和 y 分别是?你给一个函数,Python 用它给每个元素算出一个"排序键",再按键排。
例:key=len → 按长度;key=abs → 按绝对值。
True 表示降序(从大到小);默认 False 是升序。
它俩都叫关键字参数,第 6 章细讲。
key 函数在第 6 章学完函数后威力更大。b = a.copy() | b = a[:] | b = list(a)
效果完全相同,挑一个你顺眼的用。
变量名字只是标签,贴在同一个盒子上。b = a 是给盒子又贴了一张标签,不是造了新盒子。
a=[1,2,3]; b=a; b[0]=9,此时 a 是?append · index · del · sort(key=…, reverse=…) · len/max/min · count · 字符串乘法 '='*28。
zip 与 lambda 属于"超纲尝鲜",第 6 章正式学——先感受威力。
🚀 课后升级:加入 input() 现场录入成绩 · 支持按姓名查分 · 用 in 查询是否在册。
duty = ['李雷','韩梅梅','王小明']appendremove 删掉insertindex + 索引赋值index() 查;若元素可能不存在,要先 if x in duty 保护,否则会 ValueError。y = x.sort() 得到的是 Nonesorted() 拿到排好序的新列表,原列表不变b = a 会"连坐",b = a.copy() 不会reverse=True 排出排行榜元组和列表几乎一模一样,唯一的差别是不能改。而这个"不能改",恰恰让它有了独特的价值。
决定"这是不是元组"的是逗号,不是圆括号。(42) 和 42 完全等效。
这些"一组固定搭配的值",天生就该用元组。
主要是为了可读性和避免歧义(比如函数参数里)。语法上常常可省。
type((42)) → <class 'int'>;type((42,)) → <class 'tuple'>。就差一个逗号。list() ↔ tuple() 可以来回转换。想改元组内容?先 list() 转成列表,改完再 tuple() 转回去。
索引、切片、相加、乘法、成员、len/min/max —— 全都支持。
没有 append、insert、pop、remove、sort。
也不能 x[0] = 9 改元素。只有 index、count 两个方法。
字典(第 4 章)的键必须不可变。列表不行,元组可以。
典型:用 (经度, 纬度) 作键查城市。
Python 内置的很多函数/方法返回元组,你躲不开,只能跟它打交道。
看到元组,读代码的人就知道:这组值是固定的,别去动它。这是一种自我说明的代码风格。
不可变对象在实现上可以更紧凑;也避免了被误改导致的隐蔽 bug。
| 对比项 | 列表 list | 元组 tuple |
|---|---|---|
| 写法 | [1, 2, 3] 方括号 | (1, 2, 3) 圆括号(可省) |
| 可变性 | ✅ 可变,可增删改 | ❌ 不可变,创建即定型 |
| 单元素写法 | [42] 直接写 | (42,) 必须带逗号 |
| 可用方法 | 丰富:append / insert / pop / remove / sort / reverse … | 只有 index 和 count |
| 通用操作 | 完全相同:索引 · 切片 · + · * · in · len/max/min · sorted() | |
| 能否当字典键 | ❌ 不行 | ✅ 可以(第 4 章) |
| 相互转换 | tuple(列表) → 元组 | list(元组) → 列表 |
| 典型场景 | 班级名单、成绩表、待办清单(会变) | 坐标、RGB 颜色、日期、配置(固定) |
① 需要增删改吗?要 → 列表
② 要当字典键 / 集合成员吗?要 → 元组
③ 都不确定 → 用列表(更灵活)
拿到一个序列不知道是列表还是元组?type(x) 一查便知:<class 'list'> / <class 'tuple'>
type((42)) 与 type((42,)) 分别是?tuple() 把字符串和列表转成元组TypeError: 'tuple' object does not support item assignment —— 这就是"不可变"的直接证据。y = x.sort() 得到 None(用 sorted()) · b = a 不是复制(用 a.copy() 或 a[:])
| 函数 | 描述 |
|---|---|
len(seq) | 返回序列包含的元素个数 |
list(seq) | 把序列转换为列表 |
max(args) | 返回序列或一组参数中的最大值 |
min(args) | 返回序列或一组参数中的最小值 |
reversed(seq) | 反向迭代序列(返回迭代器,需 list() 转换) |
sorted(seq) | 返回一个有序的新列表,原序列不变 |
tuple(seq) | 把序列转换为元组 |
| 方法 | 描述 |
|---|---|
append(x) | 末尾追加一个元素 |
clear() | 清空列表内容 |
copy() | 返回列表副本 |
count(x) | 统计 x 出现次数 |
extend(seq) | 用另一序列扩展列表 |
index(x) | 查 x 首次出现的位置 |
insert(i, x) | 在位置 i 插入 x |
pop([i]) | 删除并返回元素(默认末尾) |
remove(x) | 删除第一个值为 x 的元素 |
reverse() | 就地反转顺序 |
sort([key][reverse]) | 就地排序,可指定键与升降序 |
len()、sorted() 这种括号里放对象的是函数;x.append() 这种对象在前、点号连接的是方法。校运会高一(3)班有 8 名同学参加 100 米跑,成绩(秒)如下。请编写程序完成统计。
12.8 出现了几次?(count)🏆 加分项:把"姓名+成绩"配对后排序,输出前三名的姓名和成绩(参考 P52 的 zip 写法)。
sorted(times)[:3] 把"排序 + 切片"连起来用;zip(times, names) 把成绩和姓名绑在一起再排序。字符串也是一种序列,但它拥有的方法远比本章介绍的丰富。下一章我们将系统学习:如何设置格式、如何查找替换、如何拆分与合并。