上一篇结尾留了一个尾巴:for 循环能把同一段代码跑很多遍,可是“跑几遍”这件事,你得自己数好、写进 range(1, 6) 里。表格今天 200 行、明天 1500 行,没人愿意每天先数一遍再改代码。当时说,等学会了能装一串数据的容器,把 range(1, 6) 换成那份数据就行——这一篇就来补上这个容器:列表。

先看一个装不下的问题

回到最朴素的写法。假设你有五个要处理的数字:

1数据1 = 30
2数据2 = 45
3数据3 = 12
4数据4 = 88
5数据5 = 60

变量确实能“记住数据”,可一个变量名只对应一个值。五个就写五行,五百个就写五百行。更麻烦的是,写完之后你没法用一句“挨个处理一下”把它们跑一遍,因为它们之间没有任何关系,只是五个同名不同号的独立变量。

我们缺的不是“能记住东西的盒子”,而是“能按顺序记住一串东西的盒子”。

列表:一串值装进一个名字

要装一串值,把值用方括号括起来,中间用逗号隔开:

1名单 = ["张三", "李四", "王五"]

这一行装下的,是刚才要写五个变量才装得下的东西,而且只用一个名字。这个新东西叫 列表(list)。它和数字、字符串一样,是可以赋给变量的一种值,只是它里面装的不是单个值,而是一串按顺序排好的值。1

几个第一眼就该知道的性质:

  • 外面的方括号 [] 是列表的标志。缺了它,"张三" 就只是一个普通字符串。
  • 逗号是分隔符,每两项之间一个;最后一项后面不加逗号也行。
  • 顺序有意义。列表是“排好队”的,谁在前谁在后,它记得清清楚楚。
  • 里面的值可以是数字、字符串、布尔值,甚至混着放;但绝大多数时候你会让它们类型一致——一列名字就都是名字,一列金额就都是数字。

用序号取出其中一个

装进去之后得能拿单个值出来用。列表给每个位置编了号,用 索引 去取:列表名后面紧跟方括号,方括号里填位置编号。

1名单 = ["张三", "李四", "王五"]
2print(名单[0])   # 张三
3print(名单[1])   # 李四
4print(名单[2])   # 王五

这里有个和直觉不一样的地方:编号从 0 开始,不是从 1 开始。第一个元素的索引是 0,第二个是 1。1 这和上一篇 range() 从 0 开始是同一个习惯——你还记得 range(5) 给出的是 0、1、2、3、4 吧,列表也这么数。

位置第 1 个第 2 个第 3 个
索引写法名单[0]名单[1]名单[2]
取到什么张三李四王五

三个元素,有效索引就是 0、1、2。写成 名单[3],程序会直接报错 IndexError: list index out of range——它不是在说“没找到”,而是在说“这个位置根本不存在”。2

还有一个很好用的写法:索引可以是负数,意思是从右往左数。名单[-1] 取最后一个,名单[-2] 取倒数第二个。1

1print(名单[-1])   # 王五

-1 代表最后一个,这让“取列表末尾那一项”变得很方便,因为你不用去数它到底排在第几位。

用 len() 数一数有几个

想知道列表里有几项,用内置函数 len():

1名单 = ["张三", "李四", "王五"]
2print(len(名单))   # 3

len() 交回元素个数。1 把它和索引放在一起,就能推出一条很有用的关系:最后一个元素的索引是 len(列表) - 1。三个元素,最后一个是 名单[2],正好是 len(名单) - 1。记住这条,你基本不会再顺手把最后一项写成 名单[3] 了。

让 for 循环直接挨个处理

现在是这一篇的正题。上一篇里 for 是这样用的:

1for 序号 in range(1, 4):
2    print(序号)

我们讲那一篇时说过,for 真正在做的事是“从 in 右边那个东西里,一个一个取出来,装进循环变量”。range(1, 4) 交出的是一串数字。那如果 in 右边换成一个列表呢?——for 就一个一个地把列表里的元素取出来:

1名单 = ["张三", "李四", "王五"]
2for 名字 in 名单:
3    print(f"请 {名字} 确认签字。")

输出三行:请张三、请李四、请王五。

请对照上一篇记住的机制看这里发生了什么。循环的写法一个字没改:冒号、缩进、循环变量,全都照旧。变的只有一件——in 右边从“一串数字”换成了“一串数据”。每一轮开头,循环变量 名字 被赋成列表里的下一个元素,然后执行循环体,再取下一个,直到列表取完为止。2

轮次循环变量 名字这一轮打印
第 1 轮张三请 张三 确认签字。
第 2 轮李四请 李四 确认签字。
第 3 轮王五请 王五 确认签字。

这正好接上了上一篇结尾的承诺。那时你写 range(1, 6),是因为手里只有数字;现在手里的列表直接递给 for,它有多少项,循环就跑多少遍,你不用数、也不用改代码。明天表格从 200 行长到 1500 行,列表自然变长,循环跟着变——“重复次数写死”这个限制,到这里才算真正拿掉。

还有一点要分清:循环变量 名字 每轮拿到的,已经不再是 1、2、3 这样的编号,而是列表里的实际内容。所以你直接用 名字 说话就行,不用再绕一圈写成 名单[名字]。

用 append 往末尾加东西

列表建好之后还能继续长。想加一项,用 append():

1名单 = ["张三", "李四"]
2名单.append("王五")
3print(名单)   # ['张三', '李四', '王五']

append 把新的一项加到列表末尾。2 它的写法和前面见过的东西不一样,有两个点要看清。

第一,点号。名单.append("王五") 里那个点,是“让 名单 这个列表去做一件事”的意思;append 是列表自带的能力,术语叫 方法。你先把它读成“给名单追加王五”就行。

第二,它改的是名单本身,不会给你一个新列表。所以正确写法是直接 名单.append("王五"),加完之后原来的 名单 就多了一项。这里有一个新手几乎必踩的坑:

1名单 = 名单.append("王五")   # 错!

这么写的后果是,名单 被赋成了 append 的返回值——而 append 的返回值是 None(空),不是列表。2 于是 名单 从“一个列表”变成了“空”,后面再加东西就会报错。记住:append 只管往原来的列表里加,不负责把列表交回给你,所以不要写 变量 = 变量.append(...)。

把两件事合起来:一边循环,一边往里攒

append 最常出现的场合,是配着 for 循环用:先准备一个空列表当“筐”,循环里逐条判断,合格的往里放。

1原始数量 = [1, 5, 8, 2, 9, 4]
2合格数量 = []
3for 件数 in 原始数量:
4    if 件数 >= 5:
5        合格数量.append(件数)
6print(合格数量)   # [5, 8, 9]

合格数量 = [] 是个空列表,一个元素都没有,正等着往里装;[] 就是“空列表”的标准写法。循环里那条 if 每轮判断一次:够 5 件,就把这一项放进筐里。跑完,筐里就是 [5, 8, 9]。

这一段其实和上一篇的累加账本是同一个套路。你还记得那个 合计 = 0 吗:准备筐的那行在循环外(否则每轮清空),干活的那行在循环内。只不过累加是拿数字往一个数上叠,这里是拿数据往一个列表里放——缩进的道理一模一样。

今天就到这里

现在你有了一个能按顺序装一串数据的容器:能按索引取其中一项,能用 len() 数个数,能用 append() 往里加,还能把整个列表直接交给 for 挨个处理。上一篇那句“重复次数写死”的抱怨,到这里有了第一批解法。

不过这一篇里的数据还是你一个个手打进方括号里的。真实的表格不在代码里,而是躺在某个 .csv 或 .xlsx 文件里。怎么把它读进来、变成这样一个能交给 for 的列表,是下一步要解决的事。