第 4 讲 · 讲义
条件、循环与函数
课件的每一页,配上讲这一页时说的话。幻灯片是网页原生渲染的,文字可以选中、可以搜索,也可以直接打印。
前三讲你写的程序都是一条直线:第一行、第二行、第三行,跑完结束。这样的程序做不了什么——它对输入毫无反应,每次运行都干同样的事。
这一讲加两样东西:分岔和重复。加完之后,你手里就是一门图灵完备语言的全部控制结构了。
这个故事之所以讲不完,是因为它把自己套在了自己里面。
换成程序的说法:怎么让一段代码重复执行?
以你现在会的东西,唯一的办法是把那行 print 复制一百遍。一百遍还能忍,一万遍呢?要重复的次数得等程序跑起来才知道呢?
这一讲的后半段解决这个问题。前半段先解决另一个更基本的问题:怎么让程序根据情况走不同的路。
两段代码逐字符对比,唯一的区别是最后一行前面多了四个空格。
请先自己判断:它们的输出差多少?有没有一段会报错?
这一讲的最后一段专门讲缩进。现在先把答案收上来。
开课先收一道题,不公布。等讲完缩进,再把这一轮的分布放出来对照。
缩进是 Python 最省事的设计,也是它最容易出低级错误的地方。
现在把答案收上来,但不公布。这一讲的最后一段讲缩进,讲完再回到这里,把开课时的分布放出来。
提示一句:两段都不报错。 这正是它麻烦的地方。
上一讲的五段:变量、字符串、类型转换与输入、格式化输出、函数。
和今天关系最大的是最后两条。今天讲的条件和循环,在真实的程序里几乎总是写在函数体内——所以「定义不等于运行」和「函数内部一律 return」这两条今天会一直用着。
特别是 return,今天讲到嵌套循环时会看到它的一个新用途:break 只能跳一层,而 return 一次跳出所有层。
第一条的内存模型今天用得不多,但第 5 讲讲列表时是核心。
OVERVIEW
Today
五段。前两段是这一讲的主体,也是整门课后面所有内容的地基——第 5 讲之后的每一段代码里都会有循环。
第三段的位运算是第 2 讲那句「一切都是 bit」的兑现,内容不多,但优先级那个坑必须讲。
第四段接上第 3 讲的函数。第五段的类型提示是一个从这一讲开始要养成的习惯,讲义和参考解答从今天起都会带类型标注。
这句话值得单独放一页。
你到今天为止只学了「顺序」这一样。加上这一讲的分岔和重复,三样凑齐——而这三样已经足以表达任何可计算的过程。
这是结构化程序定理的结论,1966 年由 Böhm 和 Jacopini 证明。它的意思是:后面你会学到的函数、递归、类、模块,没有一样在能算什么这个层面上扩展了这门语言的能力,它们扩展的是你能把多复杂的程序写清楚。
换句话说,这一讲之后,剩下的全是关于「怎么把话说明白」。
第 1 到 7 周执行 AI 政策的第 0 级:不允许 AI 生成或补全代码,请关闭编辑器里的补全功能。
可以用 AI 解释概念、查文档、读报错,但不能让它替你写代码。
这一讲尤其如此。循环是需要在脑子里模拟执行的东西——变量每一轮变成什么、什么时候退出。这种能力只能靠自己写、自己跑错、自己盯着改出来。让 AI 补一个循环,你什么也不会得到。
PART 1 OF 5
Conditions
六个比较运算符:大于、小于、大于等于、小于等于、等于、不等于。结果一律是 True 或 False。
第 2 讲讲过它们,也讲过 = 是赋值、== 才是比较。这个区分在这一讲会变得更要紧,因为 if 后面写错一个等号,报错信息不一定指得清楚。
字符串也能比较,按第 3 讲讲的字典序。
第一行里 and 先算:False and False 得 False,然后 True or False 得 True。
第二行加了括号,顺序反过来:True or False 得 True,再 True and False 得 False。
两行只差一对括号,结果相反。
和算术优先级一样,这里的建议永远成立:不确定就加括号。a or b and c 读者要停顿一下,a or (b and c) 一眼就明白。加括号不花钱。
Python 允许把比较串起来,写法和数学一样,含义也和数学一样。
这是 Python 相对 C、Java 的一个实实在在的优点。那些语言里 1 < x < 10 是合法的,但它先算 1 < x 得到一个布尔值,再把这个布尔值和 10 比较——结果毫无意义,而且不报错。
还有一个细节:链式比较里的中间值只求值一次。1 < f(x) < 10 只调用一次 f,展开写成 and 的形式就调用两次了。函数开销大的时候,这个差别是实打实的。
三件事要注意:条件后面的冒号不能少;分支体要缩进四个空格;从上往下第一个为真的分支被执行,其余全部跳过。
最后一条最重要,它解释了为什么第二个分支可以写成 score >= 80,而不必写成 80 <= score < 90——能走到那一行,就说明 score >= 90 已经是假的了。
这不只是少打几个字。条件写重复了,两处的边界就可能对不上,而这种错误极难发现。
先把上一讲结尾那道题结掉:grade(95) 打印的是「及格」,不是「优秀」。
95 分先满足了 score >= 60,于是走进第一个分支返回「及格」,后面的 elif 根本不会被检查。
这是「第一个为真的分支被执行」这条规则的直接后果,也是初学阶段最常见的逻辑错误之一。它不报错,只有在你拿一个九十分的例子去试的时候才会暴露。
可操作的做法:写多路分支时,先把所有分支的条件按严格程度排个序,从最严格的写起。分数、等级、税率这类问题都适用。
闰年规则有三层,正好用来练多路分支。
这里的写法是从最特殊的条件开始,一路 return 出去:先看能不能被 400 整除,能就直接返回 True,函数结束;再看 100,再看 4。
这叫早返回。它的好处是每一层判断完就走人,读的人不需要在脑子里同时维护多个未决的分支。
1900 年不是闰年,2000 年是——这两个正好是规则里最特殊的两种情况,测试的时候要专门挑它们。
左边是嵌套写法。四层缩进,读的人要一直记着自己在哪一层,而且 else 分支离对应的 if 很远。
右边是一行写完。短,但要把三个条件的逻辑关系在脑子里同时拼出来,改起来也容易出错。
三种写法计算结果完全相同,机器不在乎你写哪种。选早返回那种的理由只有一个:人读得最省力。
一条经验:嵌套超过两层,通常意味着可以重构。先看看能不能改成早返回。
格式是「值1 if 条件 else 值2」。条件为真取前一个值,否则取后一个。
注意它是一个表达式,会算出一个值,所以可以直接赋给变量、写进 f-string、当作函数的参数。这是它和 if 语句的区别。
适用范围很窄:只有两种情况,而且两个值都很短。一旦想嵌套,就说明该换成正经的 if-elif-else 了。
match-case 是 Python 3.10 引入的语法,用来替代一长串 elif。
写法上比 elif 整齐:要比的那个值只写一次,各个分支平铺在下面。
| 把多个值合成一个分支。_ 是通配符,放在最后接住所有没匹配上的情况,作用相当于 else。
但它真正强于 elif 的地方不在这里,在下一页。
这一页是 match 和 if 的本质区别。
case (0, y) 不只是在比较。它同时做两件事:检查这个值是不是一个两元素的结构、第一个元素是不是 0;如果是,就把第二个元素绑定到变量 y 上,分支体里可以直接用。
用 if 写同样的逻辑,要先判断类型、再判断长度、再取下标、再赋值,四步。
圆括号那个写法叫元组,第 6 讲讲。这里知道它是「两个值凑一对」就够了。
提醒一句:别过度使用。 只是比较几个数值时 if-elif 更直白。这门课不考复杂的模式匹配,看得懂基本用法就够了。
PART 2 OF 5
Loops
while 后面跟一个条件,条件为真就执行一遍循环体,然后回到条件重新判断,直到条件为假才往下走。
三个要素缺一不可:循环变量要先初始化;条件要能变成假;循环体里必须有能改变条件的语句。
这里用了第 3 讲讲的 end 参数,让五个数打在一行里。最后那个空的 print() 是为了补一个换行。
漏掉更新语句,条件就永远为真,程序会一直打印下去。
这一页是这份课件里唯一没有输出面板的代码页。其余每一页的输出都是构建时真跑出来的,而这一段跑不完。
死循环不报错,程序看起来在正常工作,只是永远不结束。在自己电脑上遇到了,按 Ctrl+C 中断。
下一页开始讲 for。能用 for 就用 for,很大一部分原因就是 for 不会漏掉更新。
for 的意思是「把后面那个东西里的元素逐个取出来,每取一个执行一遍循环体」。
字符串是可以逐个取出的——取出来的是一个个字符。方括号括起来的那个叫列表,第 5 讲专门讲。
in 后面还可以跟元组、字典、集合,以及很多别的东西。它们背后的共同机制叫可迭代对象,第 13 讲会讲。
注意 for 不需要你写初始化和更新,取完自动结束。这就是它比 while 安全的原因。
range 生成一串整数。一个参数是从 0 到 n-1,两个参数是从 a 到 b-1,三个参数多一个步长。
含头不含尾,和切片是同一个约定。所以 range(5) 有 5 个数,range(a, b) 有 b-a 个数——长度正好等于两个端点之差。
步长可以是负数,倒着走。
注意每一行都套了一个 list()。原因在下一页。
直接打印 range(5),得到的是 range(0, 5) 而不是那五个数。因为 range 并不真的把数存下来,它只记住起点、终点和步长,需要下一个的时候现算。
所以 range(1000000) 几乎不占内存,而一个一百万个数的列表要占几十兆。这个机制叫惰性求值,背后的东西叫生成器,第 13 讲讲。
想看到里面的内容,就用 list() 把它展开——这只是为了打印,实际写循环时不需要,直接 for i in range(...) 就好。
下面三行是 range 最常见的用法:从 1 加到 100。高斯当年心算出来的那个数,这里三行代码。
判断标准只有一条:开始循环之前,你知不知道要循环多少次。
知道就用 for。遍历一个集合也属于这一类——集合有多少个元素是确定的。
不知道就用 while。典型场景是读用户输入直到输入合法,或者迭代逼近某个精度——什么时候够了,得算出来才知道。
实践中大多数循环是 for。优先用 for 的理由是它把初始化和更新交给了语言,你少了两个可能出错的地方。
规则很简单:偶数除以 2,奇数乘 3 加 1,一直做下去。
这里必须用 while,因为要走几步事先算不出来——这正是 while 存在的理由。
从 27 出发要走 111 步,中间会冲到 9232 再掉下来。从 6 和 7 出发只要几步。规律看不出来。
这个问题叫考拉兹猜想,1937 年提出,至今没有证明。陶哲轩 2019 年证明了「几乎所有」正整数都会回到 1,但完整的证明还没有。
一个九行的程序,背后是一个未解的数学问题。
左边找 100 的第一个大于 1 的因数。找到就打印并 break,循环立刻结束,后面的数一个都不看。
右边打印 1 到 10 里不能被 3 整除的数。遇到 3 的倍数就 continue,跳过本轮剩下的 print,直接进入下一轮。
两个都很常用。判断标准:这一轮不要了,用 continue;整个循环都不要了,用 break。
break 只对它所在的那一层循环有效。左边这段里,内层每次都在 j 等于 1 时跳出,但外层照样跑完三轮。
初学阶段经常有人以为 break 会跳出所有层,然后对着结果百思不得其解。
正确的做法在右边:把嵌套循环抽成一个函数,用 return 跳出。第 3 讲讲过 return 会立刻结束整个函数,不管它嵌在多深。
往年的课件里有一条编程规范:for 嵌套不超过两层,尤其是里面有 return 或 break 的时候。超过两层,第三层的逻辑就该抽成函数了。
这是 Python 特有的语法,很多人写了几年也不知道。
规则只有一条:循环正常跑完,else 执行;被 break 打断,else 不执行。
这里 15 在 i 等于 3 时找到因数,break 出去,else 不执行;17 一直找到底也没找到因数,循环正常结束,else 执行,打印「是质数」。
它顶掉的是一个 flag 变量。不用 for-else 的写法要先设 found = False,找到就置 True 并 break,循环后再判断 flag。for-else 省掉了这个变量。
else 这个词在 if 语句里的意思是「条件不成立」,在循环里的意思完全不同,是「没被 break 打断」。同一个关键字两种含义,这是一个公认的设计失误。
Python 之父 Guido 后来也说过,如果重来一次不会这么设计。社区里有人建议改叫 nobreak,但改关键字会破坏兼容性,所以一直没改。
这门课的要求是:看得懂就行。 读别人的代码会遇到它,读不懂会误判逻辑。但自己写的时候,多一个 found 变量换来读者不困惑,这笔交易是划算的。
PART 3 OF 5
Bitwise operators
第 2 讲说过一切都是 bit。位运算符就是直接操作这些 bit 的工具。
前四个是逻辑运算,逐位进行:与、或、异或、取反。异或的规则是「不同为 1」,它有一个有用的性质——一个数异或同一个数两次会变回自己,加密和交换变量都用得上。
后两个是移位。左移一位相当于乘 2,右移一位相当于整除 2,道理和十进制里小数点移位相当于乘除 10 一样。
这一段内容不多,但最后那个优先级的坑必须讲。
把 a 和 b 按二进制对齐打出来,逐列比对就能看清楚每个运算符做了什么。
与:只有第一列两个都是 1,所以结果是 1000。 或:有 1 就是 1,结果是 1010。 异或:不同才是 1,第二列和第四列不同,结果是 0110。
这一页的关键其实是格式说明 04b。不补零到同样的位数,位和位就对不齐,这种表就没法看。第 3 讲讲过这个写法,这里是它的第一个实际用途。
左移一位等于乘 2,右移一位等于整除 2。注意是整除——5 右移一位得 2 不是 2.5。
下面那个是位运算最常见的实用技巧:n & 1 把除了最低位以外全部清零,只留最低位。二进制里最低位是 1 就是奇数。
它和 n % 2 == 1 完全等价。理论上位运算更快,但在 Python 里差别小到测不出来。
可读性优先,平时用 % 就好。 讲它是因为你会在别人的代码里读到,尤其是从 C 移植过来的那些。
算术运算符比位运算符结合得更紧,这一点非常反直觉,因为在多数人的心理模型里位运算是「更底层」的,应该更紧。
8 & 4 + 1 里,加法先算,变成 8 & 5,结果是 0。如果你以为是 (8 & 4) + 1,你会期待 1。
往比较那头看反而符合直觉,n & 1 == 1 确实是先算与。但这一点和 C 语言相反——C 里比较优先级更高,同一行代码在两种语言里含义不同。从 C 移植代码时这是个真实的坑。
结论很简单:不要背优先级表,加括号。
PART 4 OF 5
More on functions
在参数后面写等号和一个值,就给了它默认值。调用时不传这个参数,就用默认值;传了就用传的。
默认参数的价值在于:一个函数可以同时服务「大多数情况」和「需要定制的情况」,而调用方在大多数情况下不需要关心那些参数。
你已经用过默认参数了:第 3 讲的 print(..., sep=" ", end="\n"),那两个就是有默认值的参数。
有默认值的参数必须排在没默认值的后面,否则连函数定义这一步都过不去,直接语法错误。
原因想一下就明白:如果允许 def bad(greeting="你好", name),那么调用 bad("张三") 时,那个 "张三" 该给谁?按位置它该给 greeting,可 name 又没有默认值不能空着。规则冲突,所以语言直接禁止这种写法。
这是这一讲里少数几个在写的时候就报错的错误之一,改起来很快。
这是 Python 最有名的几个坑之一,几乎每个用 Python 超过一年的人都踩过一次。
问题出在「默认值什么时候创建」。那个空列表是在 def 这一行执行的时候创建的,也就是整个程序里只创建一次。之后每次调用不传 box,用的都是同一个列表——上一次 append 进去的东西还在里面。
正确写法是用 None 当默认值,进函数后再判断、再创建。这样每次调用都是一个新列表。
列表是第 5 讲的内容,完整解释在第 14 讲。现在记住这条结论就够了:默认值只用不可变的东西——数、字符串、None、布尔值。
三种调用方式结果完全一样。第一种按位置,第二种全部按名字,第三种混用。
混用时位置参数必须写在前面。这条规则的道理是:Python 得先把按位置对应的那些认领完,剩下的才能按名字分配。如果位置参数夹在关键字参数中间,它该对应第几个就说不清了。
什么时候用哪种,有个经验:参数只有一两个、含义很明显时用位置;参数多、或者有几个类型相同容易搞混时用名字。
参数个数事先不确定的时候用它。一个星号加一个名字,调用时给多少个位置参数,都会被打包成一个元组交给它。
注意 type 打出来是 tuple,元组,第 6 讲讲。现在把它当成一个「不能改的列表」理解就够了。
一个也不传也可以,收到的是空元组,sum 得 0。
你已经用过接受任意多个参数的函数了:print、max、min 都是。
两个星号把所有没被认领的关键字参数收成一个字典,键是参数名,值是传进来的值。字典是第 6 讲的内容。
三种参数可以一起用,顺序是固定的:普通参数、然后 *args、最后 **kwargs。
这里 1 给了 first,2 和 3 被 rest 收走,两个关键字参数被 opts 收走。
args 和 kwargs 这两个名字没有语法意义,换成别的也能跑。但请沿用它们——所有人都这么写,换名字只会让读者多花时间确认你没有别的用意。
这和 C++、Java 不同。那些语言里同名不同参数的函数可以共存,叫重载。Python 没有这个机制。
在 Python 里,def 本质上是一个赋值:把函数体绑到那个名字上。第二个 def f 就是把 f 重新绑到新函数上,原来那个没人引用了,就消失了。这和 x = 1 之后 x = 2 是一回事。
所以 f(3, 4) 正常,f(3) 报 TypeError,说少了一个参数。
想「根据参数个数做不同的事」,用默认参数或者 *args。
实践中的影响:一个文件里不要有两个同名函数。复制粘贴改一改的时候特别容易犯,而且不报错,只是有一个函数悄悄失效了。
PART 5 OF 5
Type hints and indentation
冒号后面写参数的类型,箭头后面写返回值的类型。这个语法从 Python 3.5 开始有。
读法是:这个函数接受一个浮点数,返回一个浮点数。不需要读函数体就知道它的接口长什么样。
下一页有一个重要的限制。
传一个字符串进去,Python 一声不吭地接受了,直到执行到乘法那一行才报错。
这一点必须说清楚:类型标注对 Python 解释器来说基本是注释,它不做任何检查。写了标注不等于类型安全。
报错信息是 can't multiply sequence by non-int of type 'float'——注意它说的是字符串乘法的问题,完全没提你标注了 float。
既然不强制,为什么还要写?下一页。
第一条,文档不会过期。写在注释里的「这个参数是个整数」,改代码时很容易忘记同步,于是注释开始骗人。类型标注就在参数旁边,改参数时不可能看不见。
第二条,工具能用上它。VS Code 装了 Python 扩展之后,有标注的函数会有更准的补全,传错类型当场标红,不用等运行。
第三条第 15 讲会展开。现在建立一个意识:当你让 AI 写一个函数,最有效的做法不是描述一段话,而是先把签名和类型写出来让它填实现。下一页有例子。
这三行就是一份完整的规格:两个参数都是整数列表,返回值也是整数列表,那句文档说明了行为。
把这三行给 AI,它填出来的实现基本不会跑偏。换成一段自然语言描述——「写一个函数把两个排好序的列表合起来」——它就要猜:元素是什么类型?返回新列表还是改原来的?输入没排序怎么办?
三个点是 Python 的一个合法语句,表示「这里先空着」,常用在写好签名还没写实现的时候。
课程约定:从这一讲开始,讲义和参考解答都会带类型标注。你的作业不强制要求,但强烈建议养成习惯。
四个例子覆盖了你现在会用到的绝大多数情况。
f2 里的 list[int] 表示「元素是整数的列表」。方括号里写元素类型,这个写法在 dict、tuple 上也一样。
f3 说明默认值和类型标注可以一起写,顺序是参数名、冒号、类型、等号、默认值。
f4 标了 -> None。第 3 讲讲过,没有 return 的函数返回 None,所以它的返回类型就是 None。不要因为「没有返回值」就不写,写出来才说明你是有意为之,而不是忘了。
这就是开场那道题。
左边的 print 顶格写在 for 外面,整个循环跑完才执行一次。右边缩进了四个空格,属于循环体,每一轮都执行,跑了三轮就打三遍。
注意右边输出的顺序:第一行就是「检查完毕」。因为 50 那一轮虽然不及格、不打印「及格」,但循环体里那句 print 照样执行。
关键在于两段都不报错。Python 认为两种缩进都是合法的程序,只是含义不同。你得自己看出来。
这是 Python 用缩进划分代码块所付出的代价:缩进错了,程序的含义就变了,而且往往不报错。
对应的习惯是:写完循环,回头确认每一行的缩进层级是你要的那一层。尤其是循环体最后几行。
这是一个纯粹的低级错误,但它值得单独讲一页,因为它在屏幕上看不出来——两种缩进渲染出来可能一模一样。
解决办法只有一条:统一用 4 个空格。VS Code 默认会把 Tab 键转成空格,自己敲一般不会出问题。
出问题的场景基本都是复制粘贴:从网页、PDF 或者别人的代码里复制一段过来,里面混着 Tab。遇到 TabError,把那几行整个删掉重新敲一遍最快。
另外,VS Code 可以打开「显示空白字符」,Tab 和空格就能看出区别了。
第二轮小测,五道题,把今天几段各收一次。我一题一题地放,每题答完当场公布。
【第 1 题 · 下面这段代码,输入 95 会打印什么?】
考的是「第一个为真的分支被执行」这条规则,以及分支顺序写反的后果。
解这类题的方法是从上往下逐个分支代入,遇到第一个为真的就停,后面的一律不看。
如果这道题的正确率不高,回去把闰年那三种写法再读一遍。
【第 2 题 · 下面这段循环打印什么?】
考的是 break、continue 和循环 else 三者的配合。
解法是老老实实按轮次模拟:每一轮 i 是多少、走到哪一句、是跳过本轮还是跳出整个循环。循环的题不能靠猜,要在纸上走一遍。
这个「在纸上模拟执行」的习惯,后面讲递归时会更加重要。
【第 3 题 · 6 & 3 + 1 的值是多少?】
考的就是上一页那个优先级。加法比按位与结合得紧,所以先算 3 + 1 得 4,再算 6 & 4。
6 是二进制 110,4 是 100,按位与得 100,也就是 4。
如果按 (6 & 3) + 1 算,6 & 3 是 110 与 011 得 010 也就是 2,加 1 得 3。两个答案都在选项里。
【第 4 题 · show(1, 2, mode="fast") 里,rest 是什么?】
考的是三种参数怎么分配。规则是:先按位置把普通参数填满,剩下的位置参数进 *args,所有关键字参数进 **kwargs。
1 给 first,2 进 rest,mode 进 opts。所以 rest 是只有一个元素的元组。
注意单元素元组的写法是 (2,) 带一个逗号,不带逗号的 (2) 只是加了括号的整数 2。第 6 讲会讲这个细节。
【第 5 题 · 回到开场那道题 —— 两段的输出差多少?】
回到开场那道题。先公布现在这一轮的结果,再把开课时那一轮放出来。
左边打印两行「及格」加一行「检查完毕」,共三行;右边打印两行「及格」和三行「检查完毕」,共五行。两段都不报错。
如果开课时有不少人认为其中一段会报错,现在没有了,这一段就讲到位了。
八条。第二条和第四条是这一讲最容易在实际写代码时出问题的,而且都不报错。
第三条会跟着你整个学期。从第 5 讲开始,几乎每一段代码里都会有 for。
第七条那个可变默认值的坑,第 5 讲讲完列表之后会更有体会,完整解释在第 14 讲。
第八条的类型提示从今天开始是课程约定,本周的实验要求所有函数都带类型标注。
A 段七道题。
4-1 考的是分支顺序:多路分支要从最严格的条件开始写,写反了前面那条会把后面的全吃掉,而且不报错。
4-2 不许转成字符串,就是逼你用今天讲的整除和取余把数位一位位剥出来。4-3 和 4-4 都是典型的循环题,4-4 那个式子还有个额外的好处:至今没人证明它对所有正整数都会停下来。
4-5 用到第 3 讲的格式说明,把宽度和对齐结合起来才排得整齐;注意末尾不要多一个换行,判题是按字符比的。
4-7 是下周个人作业 1 的预热——汉诺塔。这周只要求算出最少步数,先把规律找出来,下周再写完整的解。
从这次开始,所有函数都要求写类型提示,这是课程约定。
A 段之外还有 B 段和 C 段,三段同时开放。C 段是命令行基本功,期末要考。
交到「小作业 2」——它覆盖第 4 到 6 周,10 月 27 日截止。
这一讲的循环解决了「重复做一件事」,但每次处理的还是单个的值。
下一讲讲列表——把一批数据装在一起,当成一个东西来传递和处理。循环和列表是天生一对:循环负责逐个处理,列表负责装。
今天讲的 for 在下一讲会立刻变成主力。今天提到但没展开的元组、字典,第 6 讲讲。
另外,今天那个「默认值不要用可变对象」的坑,下一讲学完列表之后你就能真正理解它为什么危险了。


