Python 的 for 循环就是“for each”
如果你用过 C 风格的语言,会发现 Python 的 for 循环长得不太一样。这里没有 for (i = 0; i < n; i++) 这种计数器写法。Python 的 for 严格来说是一个 for each 循环:你给它一个集合,它一次交给你一个元素。
这会打印三行。Python 读取列表,依次取出每个元素,在循环体运行时把它绑定到 color 上。没有剩余元素时,循环结束。
注意它的结构:for <name> in <iterable>:,后面跟着缩进的循环体。缩进、冒号、循环体,和 if 的结构一样。
哪些东西是可迭代的
几乎任何存放一系列值的东西都是。列表、元组、字符串、range、字典、集合、文件,以及你会编写的大多数自定义对象。
这种统一性正是它的好处:一种语法,适用于多种数据结构。
用 range() 计数
当你确实需要计数器时,比如“做这件事十次”或者“遍历索引 0 到 9”,就用 range():
这会打印 0、1、2、3、4。range(stop) 从 0 开始计数,直到 stop 为止,但不包括 stop。range(start, stop) 可以设置起始值,range(start, stop, step) 可以控制步长:
后面会有一页专门讲 range,因为它值得深入了解一下。目前你只需要知道,range(n) 会给你 n 次迭代,编号从 0 到 n-1。
Python enumerate:带索引的循环
一个常见需求是:“我想同时拿到索引和值。”初学者的第一反应是用 range(len(...)),但 Python 有更好的工具:
enumerate() 会生成 (index, value) 对。for 后面的两个变量会把这一对值解包成两个名字:index 和 color。这比下面的写法清爽得多:
如果你想从 1 而不是 0 开始计数,传入 start=1:
Python zip:同时遍历两个序列
当你有两个长度相同的列表,想把它们一一配对时:
zip 在最短的序列结束时停止。如果你想让它继续下去并用 None 填充,可以用 itertools.zip_longest。
遍历字典
遍历字典时,你可以选择遍历什么:
大多数时候你想要的是 .items():一行可读的代码就能同时拿到键和值。
break 和 continue
有时你想提前退出循环,或者跳过某一次迭代。
break 立即退出循环:
一旦找到大于 6 的数字,循环就会停止,剩下的元素都会被忽略。
continue 跳过当前这次迭代的剩余部分,进入下一次:
两者都可以放心使用,不过如果一个循环里有多个 continue,代码会变得难以理解。很多时候,一个简单的 if/else 读起来更清楚。
循环的 else 子句
Python 有一个大多数语言没有的特性:循环可以带 else 子句。当循环没有遇到 break 而正常结束时,它就会运行。
很有用,但一开始容易让人困惑。不必勉强自己使用它:对新读者来说,在循环外用一个布尔标志往往更清楚。
一边遍历一边修改列表
有一个陷阱值得特别提醒:不要在遍历列表的同时往里添加或删除元素。迭代过程会搞不清哪些索引仍然有效,结果要么漏掉元素,要么触发错误。安全的做法是构建一个新列表:
或者,等你熟练之后,用列表推导式一行就能做到同样的事,我们会在“容器类型”一章中介绍。
一个完整的小例子
把上面几样东西结合起来:读取列表、过滤、enumerate、打印:
下一篇:while 循环
当你知道要遍历什么时,for 是正确的选择。当你需要一直循环直到某个条件改变时,while 才是正确的选择,这就是下一篇的内容。
常见问题
Python 中的 for 循环是怎么工作的?
for item in sequence: 会对序列中的每个元素运行一次缩进的循环体,item 依次取每个值。序列包括列表、元组、字符串、字典、range,以及任何定义了迭代器的对象。
break 和 continue 有什么区别?
break 立即退出循环,跳过剩下的所有迭代。continue 跳过当前这次迭代的剩余部分,直接进入下一次。break 表示“我做完了”;continue 表示“这个跳过”。
Python 中怎么带索引遍历?
把序列放进 enumerate(...),就能得到 (index, value) 对:for i, item in enumerate(items):。这比自己维护一个计数器、在循环末尾写 i += 1 清爽得多。