
题目考点
这道题主要考这几个知识点:
- 输入处理
- 字符串分割
split() - 列表下标定位
- 循环累加
- 平均值计算
- 格式化输出,保留两位小数
这题表面上是在求平均分,真正的核心难点其实是:
列的顺序是不固定的,所以不能直接默认“第 2 列就是 MARKS”。
也就是说,这题真正考的是你能不能先从表头里找到 MARKS 在第几列,再去每一行里取对应位置的数据。
审题
输入是什么
输入一共分三部分:
第一行是一个整数 N,表示学生人数。
第二行是表头,也就是列名,包含这四个字段:
IDMARKSNAMECLASS
但是这四列的顺序不固定。
接下来有 N 行,每一行都是一个学生的数据,顺序和表头对应。
输出是什么
输出所有学生 MARKS 的平均值,并且要保留两位小数。
这题要求我们真正做什么
这题不是简单地“读入每行然后取第二个数”。
而是要先解决两个问题:
MARKS这一列到底在第几列- 每个学生这一行中,对应位置的分数是多少
找到之后,把所有分数加起来,再除以学生总人数 N。
容易忽略的点
最容易错的地方就是这个:
列顺序不固定。
例如样例 1 里可能是:
ID MARKS NAME CLASS
但样例 2 里可能变成:
MARKS CLASS NAME ID
所以不能写死成:
marks = int(row[1])
因为 MARKS 不一定永远都在下标 1 的位置。
思路提示
先不要急着写代码,先把思路理顺。
你可以按下面 3 步想:
第一步:先读表头
表头这一行很关键,因为它告诉你每一列分别是什么。
比如:
MARKS CLASS NAME ID
这时候你应该先把它拆成一个列表:
['MARKS', 'CLASS', 'NAME', 'ID']
第二步:找到 MARKS 的位置
接着要想:
MARKS 在这个列表里的下标是多少?
比如上面这个例子里,MARKS 的下标就是 0。
如果表头是:
ID MARKS NAME CLASS
那 MARKS 的下标就是 1。
所以,我们其实是在“动态定位分数列”。
第三步:每读一行学生数据,就取这一列的值
比如某一行是:
92 2 Calum 1
如果我们已经知道 MARKS 的下标是 0,那这一行的分数就是:
row[0]
如果 MARKS 的下标是 1,那就取:
row[1]
最后把所有分数加起来,除以 N 就行。
完整设计思路
这道题可以拆成非常清楚的 4 步。
第 1 步:读取学生人数
先读取 N:
n = int(input())
这个 n 后面有两个作用:
- 知道后面要读多少行学生数据
- 最后求平均分时用来做除数
第 2 步:读取表头并找到 MARKS 所在位置
读入表头:
columns = input().split()
例如输入:
MARKS CLASS NAME ID
就会变成:
['MARKS', 'CLASS', 'NAME', 'ID']
然后找到 MARKS 的下标:
marks_index = columns.index('MARKS')
这个 marks_index 就是后面取分数的关键。
第 3 步:循环读取每个学生的数据并累加分数
准备一个总分变量:
total = 0
然后循环 n 次,每次读一行:
row = input().split()
这一行也会变成一个列表。
因为我们已经知道了 MARKS 的位置,所以可以直接取:
row[marks_index]
再把它转成整数,加到总分里:
total += int(row[marks_index])
第 4 步:计算平均值并格式化输出
平均分就是:
average = total / n
题目要求保留两位小数,所以输出时要格式化:
print(f"{average:.2f}")
这里的 .2f 表示保留两位小数。
代码实现
下面给你一个适合初学者、很直白的写法。
if __name__ == '__main__':
n = int(input())
columns = input().split()
marks_index = columns.index('MARKS')
total = 0
for _ in range(n):
row = input().split()
total += int(row[marks_index])
average = total / n
print(f"{average:.2f}")
运行演示
我们拿题目的第二组样例来手动走一遍。
输入是:
5
MARKS CLASS NAME ID
92 2 Calum 1
82 5 Scott 2
94 2 Jason 3
55 8 Glenn 4
82 2 Fergus 5
第一步:读取 n
n = 5
说明一共有 5 个学生。
第二步:读取表头
columns = ['MARKS', 'CLASS', 'NAME', 'ID']
找到 MARKS 的位置:
marks_index = 0
第三步:逐行累加
第一行:
row = ['92', '2', 'Calum', '1']
MARKS 在下标 0,所以分数是:
row[0] = '92'
转成整数后加入总分:
total = 92
第二行:
row = ['82', '5', 'Scott', '2']
分数是 82:
total = 174
第三行:
row = ['94', '2', 'Jason', '3']
分数是 94:
total = 268
第四行:
row = ['55', '8', 'Glenn', '4']
分数是 55:
total = 323
第五行:
row = ['82', '2', 'Fergus', '5']
分数是 82:
total = 405
第四步:求平均分
average = 405 / 5 = 81.0
格式化保留两位小数后输出:
81.00
方法总结
这道题很适合作为一种“表头定位题”的模板来记。
以后你看到这种题目时,如果题目说:
- 列顺序可能变化
- 字段名固定但位置不固定
- 某一列要被提取出来计算
你就要立刻想到这个做法:
通用思路
先读表头
再找目标列名的位置
再按这个位置去每一行取值
可以把它记成一句话:
先找列,再取值。
这类题的固定模板
n = int(input())
columns = input().split()
target_index = columns.index('目标列名')
for _ in range(n):
row = input().split()
value = row[target_index]
这个模板以后会很常见,不只是 MARKS,也可能是 ID、NAME,或者别的字段。
易错点提醒
1. 把 MARKS 的位置写死
错误思路:
total += int(row[1])
这是假设 MARKS 永远在第 2 列,但题目明确说了列顺序不固定。
2. 忘记把分数字符串转成整数
input().split() 拿到的内容默认都是字符串。
所以:
row[marks_index]
拿到的还是 '92',不是数字 92。
必须写:
int(row[marks_index])
3. 平均值输出格式不对
题目要求保留两位小数,所以不能只写:
print(average)
更稳妥的写法是:
print(f"{average:.2f}")
本节小结
这题表面在求平均数,本质在考你能不能根据表头动态定位某一列。
真正的关键不是“怎么算平均分”,而是:
- 表头先拆成列表
- 用
index()找到MARKS - 每一行都按这个下标取分数
- 累加后求平均
- 用两位小数输出
这类题以后你一旦看到“列顺序不固定”,就要立刻想到 index()。
练习
你可以先自己做这道同类型小练习:
已知输入格式如下:
第一行是整数 n。
第二行是列名,包含 ID NAME CLASS AGE,顺序不固定。
接下来 n 行是每个学生的数据。
请输出所有学生 AGE 的平均值,保留两位小数。
提示
这道题和刚才几乎一模一样,只需要把:
'MARKS'
改成:
'AGE'
然后把每一行里对应位置的年龄取出来累加即可。
marks_index = columns.index(‘MARKS’)
先说结论:这句代码到底在干什么
marks_index = columns.index('MARKS')
这句话的作用可以直接翻译成一句大白话:
在 columns 这个列表里,去找 'MARKS' 这个元素出现的位置,然后把这个位置记录到 marks_index 里。
也就是说,它不是在找分数本身,而是在找:
“分数这一列在哪一列。”
这正是这道题最关键的一步。
先理解 columns 到底是什么
在这道题里,前面通常会有这样一句:
columns = input().split()
假设输入这一行是:
ID MARKS NAME CLASS
那么 split() 之后,columns 就会变成一个列表:
['ID', 'MARKS', 'NAME', 'CLASS']
所以这时候:
columns
不是一个字符串,而是一个列表。
你可以把它理解成:
“表头这一行,被拆成了一个一个单词,按顺序装进列表里。”
再看 .index('MARKS') 是什么意思
index() 是 列表的一个方法。
它的作用是:
查找某个元素在列表中第一次出现的位置。
例如:
a = ['apple', 'banana', 'orange']
print(a.index('banana'))
输出就是:
1
因为 'banana' 在列表里的位置是下标 1。
所以:
columns.index('MARKS')
意思就是:
在 columns 这个列表中,找到 'MARKS' 的下标。
为什么返回的是 1,而不是 2
这是初学者非常容易混乱的地方。
Python 列表的下标是从 0 开始的,不是从 1 开始。
比如:
columns = ['ID', 'MARKS', 'NAME', 'CLASS']
它们的位置实际上是:
| 元素 | 下标 |
|---|---|
'ID' | 0 |
'MARKS' | 1 |
'NAME' | 2 |
'CLASS' | 3 |
所以:
columns.index('MARKS')
返回的是:
1
不是 2。
因为 Python 认为第一个位置是 0,第二个位置才是 1。
整句代码要拆成三部分看
第一部分:columns
这是一个列表,里面装着表头名称。
例如:
columns = ['ID', 'MARKS', 'NAME', 'CLASS']
第二部分:.index('MARKS')
这是在列表中查找 'MARKS' 所在位置。
得到结果:
1
第三部分:marks_index = ...
这是把找到的位置保存到变量 marks_index 里。
于是最后:
marks_index = 1
为什么这里必须写 'MARKS',还要加引号
因为 'MARKS' 是一个字符串内容,表示我们要找的列名。
如果你写成:
columns.index(MARKS)
Python 会以为你在找一个叫 MARKS 的变量。
如果这个变量根本没有定义,就会报错:
NameError
所以这里必须写成字符串:
'MARKS'
意思是:
“我要找的是这个单词本身,不是某个变量。”
手动模拟一遍这句代码是怎么执行的
假设表头这一行输入的是:
MARKS CLASS NAME ID
执行:
columns = input().split()
之后,columns 变成:
['MARKS', 'CLASS', 'NAME', 'ID']
然后执行:
marks_index = columns.index('MARKS')
Python 会做这样的事情:
先看第 0 个元素是不是 'MARKS'
columns[0] == 'MARKS'
结果为真,所以立刻返回 0。
于是:
marks_index = 0
再看另一种情况。
如果表头是:
ID NAME CLASS MARKS
那么:
columns = ['ID', 'NAME', 'CLASS', 'MARKS']
执行:
columns.index('MARKS')
Python 会这样找:
先检查 columns[0],也就是 'ID',不是
再检查 columns[1],也就是 'NAME',不是
再检查 columns[2],也就是 'CLASS',不是
再检查 columns[3],也就是 'MARKS',找到了
于是返回:
3
最后:
marks_index = 3
为什么这一步在这道题里这么关键
因为题目明确说了:
列的顺序可以变化。
这就意味着,MARKS 这一列有时候在第 2 列,有时候在第 1 列,有时候甚至在最后一列。
所以你不能写死:
row[1]
因为这相当于你假设:
“分数永远都在下标 1 的位置。”
但题目不保证这一点。
正确做法是先用:
marks_index = columns.index('MARKS')
找到分数列的真实位置。
然后再去每一行里写:
row[marks_index]
这才是“跟着表头走”的做法。
它和 row[marks_index] 是怎么配合起来工作的
这是最关键的一层理解。
假设:
columns = ['ID', 'MARKS', 'NAME', 'CLASS']
那么:
marks_index = columns.index('MARKS')
得到:
marks_index = 1
接着某一行学生数据是:
row = ['1', '97', 'Raymond', '7']
因为 MARKS 在下标 1,所以:
row[1]
就是:
'97'
也就是说:
row[marks_index]
就等价于:
row[1]
于是你就能拿到这一行对应的分数。
所以这整套逻辑其实是:
先在表头里找到“分数列的位置”
再去每一行的同样位置拿“分数值”
你可以把它理解成:
表头负责定位,数据行负责取值。
为什么不能直接写 columns['MARKS']
因为列表和字典不一样。
columns 是一个列表,比如:
['ID', 'MARKS', 'NAME', 'CLASS']
列表取值只能用数字下标:
columns[0]
columns[1]
不能这样写:
columns['MARKS']
因为 'MARKS' 不是数字下标。
所以你必须先把 'MARKS' 变成数字位置,这正是 index() 在做的事。
如果找不到 'MARKS' 会怎么样
如果列表里根本没有 'MARKS',比如:
columns = ['ID', 'NAME', 'CLASS']
那么执行:
columns.index('MARKS')
会报错:
ValueError: 'MARKS' is not in list
意思是:
“我在这个列表里根本没找到 'MARKS'。”
不过这道题已经告诉你,列名一定是 ID、MARKS、CLASS、NAME 这几个,所以正常情况下不用担心这个问题。
你可以把这句代码翻译成中文模板
以后你看到这种代码,可以直接脑中翻译:
something_index = list_name.index('目标内容')
翻译成:
在 list_name 这个列表里,找到 '目标内容' 的位置,并把这个位置保存到 something_index 变量中。
比如:
name_index = columns.index('NAME')
意思就是:
在表头列表里,找到 NAME 这一列的位置。
又比如:
id_index = columns.index('ID')
意思就是:
找到 ID 这一列的位置。
用一个生活化比喻理解
你可以把 columns 想成教室里一排贴好的标签:
['ID', 'MARKS', 'NAME', 'CLASS']
现在老师说:“去找 MARKS 这个标签在第几个位置。”
你就从左往右看:
- 第 0 个是
ID - 第 1 个是
MARKS
所以你记住:
marks_index = 1
接着每一行学生数据,就像一排具体的信息:
['1', '97', 'Raymond', '7']
你已经知道“分数在第 1 个位置”,那就去拿第 1 个位置的值,也就是 97。
初学者最容易卡住的 3 个误区
误区 1:以为 index() 找到的是值本身
不是。
columns.index('MARKS')
找到的不是分数,不是 97,也不是平均分。
它找到的是:
MARKS 这个字段在表头中的位置。
误区 2:以为 index() 会返回第几个,从 1 开始算
不是。
Python 下标从 0 开始,所以返回的是 0、1、2、3 这种数字。
误区 3:以为这一句是在处理学生数据行
也不是。
这句代码处理的是表头,不是学生具体数据。
表头负责告诉你“哪一列是什么”,学生数据行负责告诉你“这一列的具体值是多少”。
最后把这句代码和整题主线重新连起来
这道题的主线是:
先读学生人数
再读表头
然后找到 MARKS 的位置
接着读每一行学生数据
每次取出那一列的分数
最后求平均值
所以这句代码:
marks_index = columns.index('MARKS')
它的本质作用就是:
把“列名”变成“下标”。
而只有完成这一步,你后面才能写:
total += int(row[marks_index])
如果没有这一步,你就不知道每行数据里应该取哪一个位置。
一句最关键的总结
请你把这句记成这样:
columns.index('MARKS') 不是在找分数,而是在找“分数这一列在哪儿”。
小练习
你先不要看答案,自己想一想下面这几个结果分别是什么。
已知:
columns = ['NAME', 'CLASS', 'ID', 'MARKS']
请判断:
columns.index('NAME')的结果是什么columns.index('ID')的结果是什么columns.index('MARKS')的结果是什么- 如果某一行数据是
['Tom', '5', '12', '88'],那么分数应该取row[几]
提示
先找 MARKS 在表头里的位置,再去同样的位置拿分数。
为什么 row[marks_index] 这个写法,能刚好取到每一行对应的分数
这一点其实是整道题最核心的“对应关系”问题。
很多初学者前面能接受:
marks_index = columns.index('MARKS')
但是到了下一步:
row[marks_index]
就会开始疑惑:
为什么这个下标,放到每一行学生数据里,也还能继续用?而且还刚好取到分数?
答案就在一句话里:
因为每一行数据的列顺序,和表头 columns 的顺序是完全一致的。
也就是说,表头第几个位置表示什么,每一行数据的第几个位置就放的是什么。
先抓住这道题最根本的规则
题目说的是:
后面每一行数据,都是“在各自对应的列下面”。
这句话很重要。
意思就是,如果表头是:
ID MARKS NAME CLASS
那么下面每一行就一定是:
ID对应的数据 MARKS对应的数据 NAME对应的数据 CLASS对应的数据
比如:
1 97 Raymond 7
它不是随便排的,而是严格按表头来的:
| 表头位置 | 表头内容 | 这一行对应的数据 |
|---|---|---|
| 0 | ID | 1 |
| 1 | MARKS | 97 |
| 2 | NAME | Raymond |
| 3 | CLASS | 7 |
所以你一旦知道:
marks_index = 1
那你就知道:
每一行的下标 1 位置,一定就是分数。
于是:
row[marks_index]
实际上就是:
row[1]
也就是这一行的分数 97。
先把 columns 和 row 看成两行“对齐”的列表
这是最推荐你建立的脑中画面。
假设输入是:
ID MARKS NAME CLASS
1 97 Raymond 7
执行后:
columns = ['ID', 'MARKS', 'NAME', 'CLASS']
row = ['1', '97', 'Raymond', '7']
你不要把它们看成两个毫不相关的列表。
你应该把它们看成上下对齐的两排:
| 下标 | 0 | 1 | 2 | 3 |
|---|---|---|---|---|
columns | ID | MARKS | NAME | CLASS |
row | 1 | 97 | Raymond | 7 |
这时候你就会很清楚地看到:
columns[0]对应row[0]columns[1]对应row[1]columns[2]对应row[2]columns[3]对应row[3]
所以如果 MARKS 在 columns[1],那么分数就一定在 row[1]。
这就是:
row[marks_index]
为什么能取到对应分数的根本原因。
本质上,这是“同列同下标”的关系
你可以把它总结成一句很重要的话:
在表头和数据行中,同一列的数据,永远有相同的下标。
比如表头第 2 列是 NAME,那每行第 2 列就是名字。
表头第 1 列是 MARKS,那每行第 1 列就是分数。
所以:
marks_index = columns.index('MARKS')
找到的是“分数列的下标”。
而:
row[marks_index]
取到的就是“这一行分数列上的值”。
手动模拟一遍,你就会彻底明白
情况 1:表头是 ID MARKS NAME CLASS
输入:
ID MARKS NAME CLASS
1 97 Raymond 7
拆开后:
columns = ['ID', 'MARKS', 'NAME', 'CLASS']
row = ['1', '97', 'Raymond', '7']
先找:
marks_index = columns.index('MARKS')
因为 'MARKS' 在下标 1,所以:
marks_index = 1
然后:
row[marks_index]
就变成:
row[1]
得到:
'97'
这就是分数。
情况 2:表头顺序变了
输入变成:
MARKS CLASS NAME ID
92 2 Calum 1
拆开后:
columns = ['MARKS', 'CLASS', 'NAME', 'ID']
row = ['92', '2', 'Calum', '1']
这时候:
marks_index = columns.index('MARKS')
得到:
marks_index = 0
因为 MARKS 在第 0 个位置。
接着:
row[marks_index]
就等于:
row[0]
得到:
'92'
还是正确取到了分数。
情况 3:MARKS 在最后一列
如果表头是:
ID NAME CLASS MARKS
某一行是:
1 Raymond 7 97
拆开后:
columns = ['ID', 'NAME', 'CLASS', 'MARKS']
row = ['1', 'Raymond', '7', '97']
这时:
marks_index = columns.index('MARKS')
得到:
marks_index = 3
于是:
row[marks_index]
就等于:
row[3]
得到:
'97'
依然正确。
你可以把它理解成“表头是地图,数据行是内容”
这是一个很好记的比喻。
表头 columns 是地图
它负责告诉你:
- 第 0 列是什么
- 第 1 列是什么
- 第 2 列是什么
- 第 3 列是什么
数据行 row 是具体内容
它负责告诉你:
- 第 0 列放了什么值
- 第 1 列放了什么值
- 第 2 列放了什么值
- 第 3 列放了什么值
所以表头里你一旦查出:
MARKS 在第 1 列
那么数据行里你就知道:
第 1 列放的就是分数
于是自然就可以写:
row[marks_index]
为什么不能直接写 row['MARKS']
因为 row 也是一个列表,不是字典。
比如:
row = ['1', '97', 'Raymond', '7']
列表只能用数字下标取值:
row[0]
row[1]
row[2]
不能写成:
row['MARKS']
因为 'MARKS' 不是列表下标。
所以必须分成两步:
第一步,先在表头中找出 MARKS 的数字位置:
marks_index = columns.index('MARKS')
第二步,再用这个数字位置去数据行中取值:
row[marks_index]
为什么这一步特别适合初学者记成固定模板
因为以后你会遇到很多类似题:
- 找
NAME对应的数据 - 找
ID对应的数据 - 找
AGE对应的数据 - 找
SCORE对应的数据
这种题的模板完全一样:
target_index = columns.index('目标列名')
value = row[target_index]
你要把它记成一对固定搭档:
columns.index(...)
row[...]
前者负责找“位置”,后者负责按位置“拿值”。
这其实就是“先定位,再取值”
整套逻辑可以压缩成四个字:
先定位,再取值。
详细一点就是:
- 用表头定位
MARKS在第几列 - 用同样的列号去每一行取出对应的数据
所以:
marks_index = columns.index('MARKS')
是“定位”。
而:
row[marks_index]
是“取值”。
这两句必须连起来理解,不能分开死记。
最容易出错的几个误区
误区 1:以为 row[marks_index] 是自动识别“分数”
不是自动识别。
Python 根本不知道哪个值是分数,它只是按下标取值。
真正让它“取到分数”的原因是:
你前面已经通过表头,算出了 MARKS 所在的下标。
误区 2:以为每一行数据顺序可以乱
不可以。
表头顺序可以变,但一旦表头确定了,下面每一行数据就必须按那个顺序来。
否则整个表就没有意义了。
误区 3:以为 marks_index 每一行都要重新算一次
不需要。
表头只读一次,所以 MARKS 的列位置也只要找一次。
后面每一行都沿用这个位置就行。
比如表头一旦确定 MARKS 在下标 1,那么所有学生行的下标 1 都是分数。
用最简短的一句话总结
为什么 row[marks_index] 能刚好取到每一行对应的分数?
因为:
marks_index 记录的是 “MARKS 这一列的列号”,而每一行数据都和表头按相同列顺序对齐,所以用这个列号去取,拿到的自然就是分数。
再给你一个非常值得记住的图
你以后可以在脑子里默认画成这样:
| 下标 | 0 | 1 | 2 | 3 |
|---|---|---|---|---|
表头 columns | ID | MARKS | NAME | CLASS |
数据 row | 1 | 97 | Raymond | 7 |
只要你知道 MARKS 在下标 1,那么同一列下面的数据 97 也必然在下标 1。
所以:
row[1]
就是分数。
而 row[marks_index] 只是把这个 1 写得更灵活、更通用而已。
小练习
已知:
columns = ['CLASS', 'NAME', 'MARKS', 'ID']
row = ['7', 'Raymond', '97', '1']
请你自己先想一想:
marks_index等于几row[marks_index]得到什么- 为什么它刚好是分数
提示
先看 MARKS 在表头的哪个位置,再去同样的位置看 row。
如果你愿意,我下一步可以继续补一节:
为什么 columns 和 row 虽然都是 split() 得到的列表,但一个是“字段名列表”,一个是“字段值列表”。
为什么 columns 和 row 虽然都是 split() 得到的列表,但一个是“字段名列表”,一个是“字段值列表”
这个问题问得很好。
因为从代码表面看,它们确实很像:
columns = input().split()
row = input().split()
两句长得几乎一模一样,都是:
input()读一整行split()按空格拆开- 得到一个列表
所以很多初学者会觉得:
“既然写法一样,那它们不就是同一种东西吗?”
写法一样,但含义不一样。
这正是你现在要彻底搞清楚的地方。
先说最核心的结论
split() 只负责做一件事:
把一整行字符串拆成若干个小字符串,并放进列表里。
它并不会自动判断:
- 这些字符串是列名
- 还是学生数据
- 还是命令
- 还是数字
- 还是别的内容
所以,split() 只是一个“拆分工具”。
至于拆出来的列表到底代表什么,不是由 split() 决定的,而是由:
这一行输入本身的内容和它在题目中的位置
来决定的。
先理解:同样的工具,可以处理不同性质的内容
这和剪刀很像。
剪刀可以剪纸,也可以剪布。
虽然工具是同一个,但你剪的材料不同,结果的意义就不同。
split() 也一样。
它可以拆:
- 表头这一行
- 学生数据这一行
- 命令这一行
- 数字这一行
虽然都是“拆开成列表”,但拆开的内容身份不一样。
为什么 columns 是“字段名列表”
来看题目的第二行。
题目说,第二行包含列名,也就是:
IDMARKSNAMECLASS
只是顺序可能变化。
例如输入可能是:
ID MARKS NAME CLASS
执行:
columns = input().split()
之后得到:
['ID', 'MARKS', 'NAME', 'CLASS']
你会发现,这个列表里的元素,不是具体某个学生的数据,而是:
每一列的名字。
所以 columns 的作用不是存数据内容,而是告诉你:
- 第 0 列是什么
- 第 1 列是什么
- 第 2 列是什么
- 第 3 列是什么
因此它叫“字段名列表”最合适。
这里的“字段名”,你可以理解成“列标题”“列标签”。
为什么 row 是“字段值列表”
接着看后面的某一行学生数据。
比如:
1 97 Raymond 7
执行:
row = input().split()
之后得到:
['1', '97', 'Raymond', '7']
这个列表里放的就不是列名了,而是某一个学生在各列上的具体内容:
1是这个学生的 ID97是这个学生的 MARKSRaymond是这个学生的 NAME7是这个学生的 CLASS
所以,row 存放的是:
这一行每个字段对应的实际值。
因此它是“字段值列表”。
两者的区别,不在代码写法,而在“这一行代表什么”
这是你必须建立起来的思维方式。
下面这两句代码:
columns = input().split()
row = input().split()
虽然写法很像,但本质区别不在 split(),而在于:
第一行输入代表的是表头
所以拆出来的是字段名列表:
['ID', 'MARKS', 'NAME', 'CLASS']
第二行输入代表的是某个学生的数据
所以拆出来的是字段值列表:
['1', '97', 'Raymond', '7']
也就是说:
同样是“拆成列表”,但拆出来的那一行本来就不是一类信息。
用“表格”去理解会特别清楚
你可以把整个输入想成一个表格。
比如:
| ID | MARKS | NAME | CLASS |
|---|---|---|---|
| 1 | 97 | Raymond | 7 |
| 2 | 50 | Steven | 4 |
| 3 | 91 | Adrian | 9 |
在这个表格里:
第一行是列标题
也就是:
| ID | MARKS | NAME | CLASS |
它描述的是:
“每一列叫什么名字。”
这对应:
columns = ['ID', 'MARKS', 'NAME', 'CLASS']
后面的每一行是具体记录
例如:
| 1 | 97 | Raymond | 7 |
它描述的是:
“某一个学生在这些列上的具体值是什么。”
这对应:
row = ['1', '97', 'Raymond', '7']
所以你会发现:
columns是这张表的“说明”row是这张表的“内容”
为什么这两个列表必须分开理解
因为它们在程序里的用途完全不一样。
columns 的用途:负责定位
你会拿它去做:
marks_index = columns.index('MARKS')
也就是说,columns 不是拿来算总分的,而是拿来查:
哪一列是 MARKS
它更像“地图”。
row 的用途:负责取值
你会拿它去做:
row[marks_index]
也就是说,row 不是拿来查列名的,而是拿来取:
这一行在某一列上的具体值
它更像“数据”。
可以把它们记成“名字层”和“值层”
这是一个很实用的理解方式。
columns:名字层
这里存的是字段名:
IDMARKSNAMECLASS
row:值层
这里存的是字段值:
197Raymond7
所以整个程序其实是在做两层配对:
名字层 columns | 值层 row |
|---|---|
| ID | 1 |
| MARKS | 97 |
| NAME | Raymond |
| CLASS | 7 |
程序先在名字层找到:
'MARKS'
的位置,再去值层取同样位置上的数据:
'97'
split() 并不会帮你区分“名字”和“值”
这一点很重要。
split() 只是机械地把一行拆开。
例如:
'ID MARKS NAME CLASS'.split()
得到:
['ID', 'MARKS', 'NAME', 'CLASS']
而:
'1 97 Raymond 7'.split()
得到:
['1', '97', 'Raymond', '7']
你会发现,split() 完全没有“思考”,它只是把空格当成分隔符去切。
所以:
- 之所以第一个列表叫字段名列表,是因为原字符串本来就是表头
- 之所以第二个列表叫字段值列表,是因为原字符串本来就是数据行
身份来自输入内容本身,不来自 split()。
这和变量名也有关系
虽然本质不是变量名决定的,但变量名会帮助你理解。
比如:
columns = input().split()
你看到 columns,就应该想到:
这是列名、列标题、表头。
而:
row = input().split()
你看到 row,就应该想到:
这是某一行的数据内容。
所以好的变量名,其实是在提醒你:
这两个列表虽然结构相似,但职责不同。
结构相同,不代表语义相同
这是编程里很常见的一种情况。
比如下面两个列表:
['apple', 'banana', 'orange']
['Tom', '18', 'male']
它们都是列表,结构形式一样。
但第一个是水果列表,第二个是个人信息列表。
所以“长得一样”不代表“意义一样”。
在这道题里也是一样:
columns = ['ID', 'MARKS', 'NAME', 'CLASS']
row = ['1', '97', 'Raymond', '7']
它们都是列表,但一个表示字段名,一个表示字段值。
为什么这一步理解清楚之后,后面的代码就顺了
只要你明白了:
columns是列名列表row是当前这一行的数据列表
你就会很自然地理解下面这两句:
marks_index = columns.index('MARKS')
total += int(row[marks_index])
第一句是在“列名列表”里找:
MARKS 这个名字在第几列
第二句是在“数据列表”里取:
这一行第几列的实际值
这就完成了从“字段名”到“字段值”的映射。
你可以把它理解成“说明书”和“实际内容”
这个比喻也很好记。
columns 是说明书
它告诉你:
- 第 0 个位置表示什么
- 第 1 个位置表示什么
- 第 2 个位置表示什么
row 是实际内容
它告诉你:
- 第 0 个位置放了什么值
- 第 1 个位置放了什么值
- 第 2 个位置放了什么值
于是程序的思路就是:
先看说明书,找到 MARKS 在第几个位置
再去实际内容里取那个位置上的值
最后把三者关系连起来
现在你可以把这三样东西放在一起理解:
1. columns
字段名列表,负责说明每列是什么。
['ID', 'MARKS', 'NAME', 'CLASS']
2. marks_index
从字段名列表里查出来的列号。
1
3. row
字段值列表,负责提供某一行的实际数据。
['1', '97', 'Raymond', '7']
于是:
row[marks_index]
就是:
row[1]
得到:
'97'
这就是该学生的分数。
一句最值得记住的话
请把这一句记牢:
columns 存的是“这列叫什么”,row 存的是“这一列的值是什么”。
两者都是 split() 得到的列表,但一个负责描述结构,一个负责承载数据。
小练习
已知输入如下:
NAME ID CLASS MARKS
Tom 1 5 88
请你自己先想一想:
columns会变成什么row会变成什么columns里哪个元素表示“分数字段”row里哪个元素表示“Tom 的分数”
提示
先区分“列名”和“列值”,再去看它们是否处在同一个下标位置。



