6.3. 使用 for 循环迭代

与大多数其他语言一样,Python 也有 for 循环。您直到现在才看到它们,唯一的原因是 Python 擅长于许多其他事情,以至于您不需要经常使用它们。

大多数其他语言都没有像 Python 这样强大的列表数据类型,因此您最终会做很多手工工作,指定开始、结束和步长来定义整数、字符或其他可迭代实体的范围。但在 Python 中,for 循环只是简单地迭代一个列表,就像 列表推导式 的工作方式一样。

示例 6.8. 介绍 for 循环

>>> li = ['a', 'b', 'e']
>>> for s in li:         1
...     print s          2
a
b
e
>>> print "\n".join(li)  3
a
b
e
1 for 循环的语法类似于 列表推导式li 是一个列表,s 将依次获取每个元素的值,从第一个元素开始。
2 if 语句或任何其他 缩进块 一样,for 循环可以包含任意数量的行代码。
3 这就是您还没有看到 for 循环的原因:您还不需要它。令人惊讶的是,当您真正想要的是 join 或列表推导式时,您在其他语言中使用 for 循环的频率有多高。

执行“普通”(按 Visual Basic 标准)计数器 for 循环也很简单。

示例 6.9. 简单计数器

>>> for i in range(5):             1
...     print i
0
1
2
3
4
>>> li = ['a', 'b', 'c', 'd', 'e']
>>> for i in range(len(li)):       2
...     print li[i]
a
b
c
d
e
1 正如您在 示例 3.20,“分配连续值” 中看到的,range 生成一个整数列表,然后您循环遍历该列表。我知道这看起来有点奇怪,但偶尔(我强调 偶尔)需要一个计数器循环。
2 永远不要这样做。这是 Visual Basic 式的思维方式。摆脱它。只需迭代列表,如上一个示例所示。

for 循环不仅适用于简单的计数器。它们可以迭代各种事物。以下是一个使用 for 循环迭代字典的示例。

示例 6.10. 迭代字典

>>> import os
>>> for k, v in os.environ.items():      1 2
...     print "%s=%s" % (k, v)
USERPROFILE=C:\Documents and Settings\mpilgrim
OS=Windows_NT
COMPUTERNAME=MPILGRIM
USERNAME=mpilgrim

[...snip...]
>>> print "\n".join(["%s=%s" % (k, v)
...     for k, v in os.environ.items()]) 3
USERPROFILE=C:\Documents and Settings\mpilgrim
OS=Windows_NT
COMPUTERNAME=MPILGRIM
USERNAME=mpilgrim

[...snip...]
1 os.environ 是一个字典,包含系统上定义的环境变量。在 Windows 中,这些是您可以从 MS-DOS 访问的用户和系统变量。在 UNIX 中,它们是在 shell 的启动脚本中导出的变量。在 Mac OS 中,没有环境变量的概念,因此此字典为空。
2 os.environ.items() 返回一个元组列表:[(键1, 值1), (键2, 值2), ...]for 循环迭代此列表。第一轮,它将 键1 分配给 k,将 值1 分配给 v,因此 k = USERPROFILEv = C:\Documents and Settings\mpilgrim。在第二轮中,k 获取第二个键 OSv 获取相应的值 Windows_NT
3 使用 多变量赋值列表推导式,您可以用一个语句替换整个 for 循环。您是否真的在实际代码中这样做是个人编码风格的问题。我喜欢它,因为它清楚地表明我正在做的是将字典映射到列表中,然后将列表连接成单个字符串。其他程序员更喜欢将其写成 for 循环。两种情况下的输出相同,尽管此版本稍快,因为只有一个 print 语句而不是多个。

现在我们可以看一下 第 5 章 中介绍的示例 fileinfo.py 程序中的 MP3FileInfo 中的 for 循环。

示例 6.11. MP3FileInfo 中的 for 循环

    tagDataMap = {"title"   : (  3,  33, stripnulls),
                  "artist"  : ( 33,  63, stripnulls),
                  "album"   : ( 63,  93, stripnulls),
                  "year"    : ( 93,  97, stripnulls),
                  "comment" : ( 97, 126, stripnulls),
                  "genre"   : (127, 128, ord)}                               1
    .
    .
    .
            if tagdata[:3] == "TAG":
                for tag, (start, end, parseFunc) in self.tagDataMap.items(): 2
                    self[tag] = parseFunc(tagdata[start:end])                3
1 tagDataMap 是一个 类属性,它定义了您要在 MP3 文件中查找的标签。标签存储在固定长度的字段中。读取文件的最后 128 个字节后,这些字节的第 3 到 32 个字节始终是歌曲标题,第 33 到 62 个字节始终是艺术家姓名,第 63 到 92 个字节是专辑名称,依此类推。请注意,tagDataMap 是一个元组字典,每个元组包含两个整数和一个函数引用。
2 这看起来很复杂,但事实并非如此。for 变量的结构与 items 返回的列表元素的结构相匹配。请记住,items 返回一个形式为 (, ) 的元组列表。该列表的第一个元素是 ("title", (3, 33, <function stripnulls>)),因此在循环的第一轮中,tag 获取 "title"start 获取 3end 获取 33parseFunc 获取函数 stripnulls
3 现在您已经提取了单个 MP3 标签的所有参数,保存标签数据就很容易了。您从 startendtagdata 进行 切片 以获取此标签的实际数据,调用 parseFunc 对数据进行后处理,并将其分配为伪字典 self 中键 tag 的值。迭代 tagDataMap 中的所有元素后,self 拥有所有标签的值,并且 您知道它是什么样子的