判据只有一条:同一类数据要「一起处理」,就该用数组。 不是「数据多到几十条才用」,而是「我要对每一个都做同一件事」的时候用。 反过来,几条数据各有各的用途,硬塞进数组反而看不出谁是谁。
上一篇讲了数组长什么样、下标怎么算。这一篇只回答一个问题:什么时候值得开一排盒子。 教材在这里打的比方是储物柜:一排格子,编号清楚,想开第 7 格不用从头开。 这也是全书第一次让「数据条数」进入代码——它决定了后面所有循环怎么写。
一、不用数组的日子:五个变量,五行代码
五个学生,五个分数。不开数组的话只能这样写:s1 = 88、s2 = 92,一直到 s5 = 97。
求平均写成 (s1 + s2 + s3 + s4 + s5) / 5。看着也还行,五个加号而已。
问题出在第六个学生转来的时候:得再加一行 s6 = 78,再把求平均那一行改成六个加号、除数改成 6。
演示里的表把这件事量出来了:5 条数据要写 7 行,加一条要改 2 处。 20 条要写 22 行,100 条要写 102 行——行数跟着数据条数一起长。
二、改两处听着不多,可怕的是它会繁殖
两处是「加一行声明」和「改求平均那一行」。只求平均的话,确实不算负担。 但统计口径从来不止一个。求总分要改一处,求最高分要改一处,求方差、求及格人数、求排名,各要改一处。 每加一个统计口径,就多一处要跟着数据条数一起改。五条数据时要改十处,一百条数据时要改两百处。
更致命的是漏改。漏一处不报错,只是算出来的数不对——这种错最难查,因为它看起来跑得好好的。 所以不用数组的代价,不在写的时候,在改的时候。
三、换成数组,代码里就没有「几条」这个数了
scores = [88, 92, 75, 60, 97],一行装下五条。
求平均 sum(scores) / len(scores) 得 82.40,求最大 max(scores) 得 97。
注意这里没有出现 5,也没有出现 97——它问的是数组本身。
转来第六个学生,只要 scores.append(78)。平均变成 81.67,一个字都没改。
这就是数组真正的价值:它把「数据有几条」从代码里搬进了数据里。代码只描述「怎么处理每一条」,
不描述「一共有几条」。
这句话换个说法就是:
循环的上界由 len() 给,不由你手写。后面第 4 章那些「用数组处理大量数据」的写法,
根子都在这里。
四、判据只有一条:要不要一起处理
教材给的标准很短:需要「一起处理」的时候用数组。 「一起处理」翻成人话就是:我要对每一条都做同一件事。那件事只能写在循环里, 而循环只能按下标取——所以必须先有下标。
| 情况 | 用什么 | 为什么 |
|---|---|---|
| 一个数、一个名字、一句话 | 普通变量 | 就一个,没必要开一排 |
| 同一类数据,两条以上一起处理 | 数组 | 要循环就必须能按下标取 |
| 几条数据各有各的用途 | 分别起名 | 硬塞进数组反而看不出谁是谁 |
五、数组也不是万能的
第三种情况值得多说一句。
假设你要存一个人的姓名、年龄、身高,硬塞成 [u'张三', 28, 175.5],看着挺紧凑。
但读代码的人看到 p[1] 根本不知道是年龄还是身高,
而且这个数组里的三个格子类型都不一样——数组本来是为「同一类数据」准备的。
这种场合该用「结构体」或者字典,让每个值都有名字。
第 3 章会讲到为什么「同类型」这件事对数组这么重要。
所以判据要连起来读:同一类 数据、要 一起处理——两个条件都满足,才换数组。
六、动手 Demo:不用数组要多写几行,用数组一行搞定
先摆出不用数组的写法,数一数五个、二十个、一百个数据各要写多少行。 再把同一件事用数组写一遍,看「加一条数据」时两者差在哪。 最后给一张三种情况的判据表。
# 五条数据,不用数组
s1, s2, s3, s4, s5 = 88, 92, 75, 60, 97
print((s1 + s2 + s3 + s4 + s5) / 5)
# 同样五条,用数组
scores = [88, 92, 75, 60, 97]
print(sum(scores) / len(scores), max(scores))
scores.append(78)
print(len(scores), sum(scores) / len(scores))
实跑输出:
--------------------------------------------------------------------
什么时候该用数组
--------------------------------------------------------------------
① 先看看不用数组有多笨
--------------------------------------------------------------------
五个学生要存分数,只能这样:
s1 = 88 s2 = 92 s3 = 75 s4 = 60 s5 = 97
求平均:avg = (s1 + s2 + s3 + s4 + s5) / 5
来了第六个学生:要加 s6,还得改求平均那一行
| 数据条数 | 不用数组要写几行 | 加一条要改几处 |
|---:|---:|---:|
| 5 | 7 | 2 |
| 20 | 22 | 2 |
| 100 | 102 | 2 |
改两处听着不多,但这两处会随着「求最大」「求最小」「求方差」一起长。
每加一个统计口径,就多一处要跟着改。
② 换成数组,全是一行
--------------------------------------------------------------------
scores = [88, 92, 75, 60, 97]
平均:sum(scores) / len(scores) = 82.40
最大:max(scores) = 97
追加一个:scores.append(78)
现在 6 个,平均 81.67
数据从 5 条变成 6 条,代码一个字没改。
这就是数组的价值——它把「数据有几条」变成变量,而不是写死在代码里。
③ 判断标准就一条
--------------------------------------------------------------------
| 情况 | 用什么 | 为什么 |
|---|---|---|
| 一个数、一个名字、一句话 | 普通变量 | 就一个,没必要开一排 |
| 同一类数据,两条以上一起处理 | 数组 | 要循环就必须能按下标取 |
| 几条数据各有各的用途 | 分别起名 | 硬塞进数组反而看不出谁是谁 |
教材的判据更短:要「一起处理」的时候用数组。
一起处理的意思就是「我要对每一件都做同一件事」——那件事只能写在循环里,循环只能按下标取。
①第一段是「不用数组」的样子:变量名自带序号,加法要一个个列出来。
②第二段的 sum(scores) / len(scores) 里没有出现 5,
也没有出现任何具体分数——它问的是数组本身有几条、加起来多少。
③append 之后平均从 82.40 变成 81.67,代码没动一行。这就是「数据条数不写死在代码里」。
④表里那个「加一条要改 2 处」是手数出来的,不是跑出来的:一处加声明,一处改求平均那一行。
30 秒实验
- 把
scores换成 100 条随机分数([random.randint(0, 100) for _ in range(100)]), 看求平均那行要不要改。 - 用循环求最高分(不许用
max),看循环的上界怎么写才不会写死。 - 想一个你手上「几条数据各有各用途」的例子,试试硬塞数组会乱成什么样。
常见误区
- 「数据多到几十条才该用数组。」判据不是条数,是有没有「一起处理」这回事。三条要循环也该用。
- 「用了数组,代码就短了。」短是副作用。真正的收益是加数据时不用改代码。
- 「一堆相关的东西都能塞进一个数组。」类型不同的值塞进去,每个下标代表什么就说不清了。
自测三问(点开对答案)
1. 教材给的那条判据,原话是什么? —— 需要「一起处理」的时候用数组。翻成人话:要对每一条做同一件事。 2. 为什么不用数组时,「加一条数据」的成本会越滚越大? —— 每加一个统计口径(平均、总分、最大、方差)就多一处要跟着改,漏改不报错只算错。 3. 什么情况下不该用数组? —— 几条数据各有各的用途、类型还不一样时。该用结构体或字典,让每个值有名字。数组能装一行数,那一张表格怎么装? 下一节把数组摞起来变成二维,顺手把字符串也拉进「一排格子」这个框里。
- ☐ (本篇完,下一篇 2.6 二维数组与字符串)