8.6 for语句

Python提供给我们的另一个循环机制就是for语句。它提供了Python中最强大的循环结构。它可以遍历序列成员,可以用在列表解析和生成器表达式中,它会自动地调用迭代器的next()方法,捕获 StopIteration异常并结束循环(所有这一切都是在内部发生的)。如果你刚刚接触Python那么我们要告诉你,在以后你会经常用到它的。和传统语言(例如C/C++,Fortran,或者Java)中的for语句不同,Python的for更像是shell或是脚本语言中的foreach循环。

8.6.1 一般语法

for循环会访问一个可迭代对象(例如序列或是迭代器)中的所有元素,并在所有条目都处理过后结束循环,它的语法如下:

8.6 for语句 - 图1

每次循环,iter_var迭代变量被设置为可迭代对象(序列、迭代器或其他支持迭代的对象)的当前元素,提供给suite_to_repeat语句块使用。

8.6.2 用于序列类型

本节中,我们将学习用for循环迭代不同的序列对象。样例将涵盖字符串、列表及元组。

8.6 for语句 - 图2

当迭代字符串时,迭代变量只会包含一个字符(长度为1的字符串)。但这并不常用。在字符串里中查找字符时,程序员往往使用in来测试成员关系,或者使用string模块中的函数以及字符串方法来检查子字符串。

看到单个的字符在一种情况下有用,即在通过print语句调试for循环中的序列时,如果你在应该看到字符串的地方发现的却是单个的字符,那么很有可能你接受到的是一个字符串,而不是对象的序列。

迭代序列有三种基本方法:

1. 通过序列项迭代

8.6 for语句 - 图3

在上面的例子中,我们迭代一个列表。每次迭代,eacgName变量都被设置为列表中特定某个元素,然后我们在代码块中打印出这个变量。

2. 通过序列索引迭代

另一个方法就是通过序列的索引来迭代:

8.6 for语句 - 图4

8.6 for语句 - 图5

我们没有迭代元素,而是通过列表的索引迭代。

这里我们使用了内建的len()函数获得序列长度,使用range()函数(我们将在下面详细讨论它)创建了要迭代的序列。

8.6 for语句 - 图6

使用range()我们可以得到用来迭代nameList的索引数列表;使用切片/下标操作符([]),就可以访问对应的序列对象。

如果你对性能有所了解的话,那么毫无疑问你会意识到直接迭代序列要比通过索引迭代快。如果你不明白,那么你可以仔细想想。(参见练习8-13)。

3. 使用项和索引迭代

两全其美的办法是使用内建的enumerate()函数,它是Python 2.3的新增内容。代码如下:

8.6 for语句 - 图7

8.6.3 用于迭代器类型

用for循环访问迭代器和访问序列的方法差不多。唯一的区别就是for语句会为你做一些额外的事情。迭代器并不代表循环条目的集合。

迭代器对象有一个next()方法,调用后返回下一个条目。所有条目迭代完后,迭代器引发一个 StopIteration异常告诉程序循环结束。for语句在内部调用next()并捕获异常。

使用迭代器做for循环的代码与使用序列条目几乎完全相同。事实上在大多情况下,你无法分辨出你迭代的是一个序列还是迭代器,因此,这就是为什么我们在说要遍历一个迭代器时,实际上可能我们指的是要遍历一个序列,迭代器,或是一个支持迭代的对象(它有next()方法)。

8.6.4 range()内建函数

我们前面介绍Python的for循环的时候提到过它是一种迭代的循环机制。Python同样提供一个工具让我们在传统的伪条件设置下使用for声明,例如从一个数字开始计数到另一个数字,一旦到达最后的数字或者某个条件不再满足就立刻退出循环。

内建函数range()可以把类似foreach的for循环变成你更加熟悉的语句。例如从0到10计数,或者从10到100一次递增5。

range()的完整语法

Python提供了两种不同的方法来调用range()。完整语法要求提供两个或三个整型参数:

8.6 for语句 - 图8

range()会返回一个包含所有k的列表,这里start<= k < end,从start到end,k每次递增step。step不可以为零,否则将发生错误。

8.6 for语句 - 图9

如果只给定两个参数,而省略step,step就使用默认值1。

8.6 for语句 - 图10

我们来看看解释器环境下的例子。

8.6 for语句 - 图11

我们的循环从2 “数”到19,每次递增3。如果你熟悉C的话,就会发现,range()的参数与C的for循环变量有着直接的关系:

8.6 for语句 - 图12

虽然看起来像是一个条件循环(检查eachVal<19),但实际上是range()先用我们指定的条件生成一个列表,然后把列表用于这个for语句。

range()简略语法

range()还有两种简略的语法格式:

8.6 for语句 - 图13

我们在第2章看到过最短的语法接受一个值,start默认为0,step默认为1,然后range()返回从0到end的数列。

8.6 for语句 - 图14

range()的中型版本和完整版本几乎完全一样,只是step使用默认值1。现在我们在Python解释器中试下这条语句:

8.6 for语句 - 图15

8.6 for语句 - 图16

8.6 for语句 - 图17核心笔记:为什么range()不是只有一种语法?

你已经知道了range()的所有语法,有些人可能会问一个挑剔的问题,为什么不把这两种语法合并成一个下面这样的语法?

8.6 for语句 - 图18

这个语法不可以使用两个参数调用。因为step要求给定start。换句话说,你不能只传递 end和step参数。因为它们会被解释器误认为是start和end。

8.6.5 xrange()内建函数

xrange()类似range(),不过当你有一个很大的范围列表时,xrange()可能更为适合,因为它不会在内存里创建列表的完整拷贝。它只被用在for循环中,在for循环外使用它没有意义。同样地,你可以想到,它的性能远高出range(),因为它不生成整个列表。在Python的未来版本中,range()可能会像 xrange()一样,返回一个可迭代对象(不是列表也不是一个迭代器)。

8.6.6 与序列相关的内建函数

sorted()、reversed()、enumerate()、zip()

下边是使用循环相关和序列相关函数的例子。为什么它们叫“序列相关”呢?是因为其中两个函数(sorted()和zip())返回一个序列(列表),而另外两个函数(reversed()和enumerate())返回迭代器(类似序列)。

8.6 for语句 - 图19

8.6 for语句 - 图20

我们已经涵盖了Python中的所有循环语句,下面我们看看循环相关的语句,包括用于放弃循环的 break语句和立即开始下一次迭代的continue语句。