一、迭代器
我们知道 for 循环可以用于 Python 中的任何序列类型,实际上 for 循环可用于Python 中的任何可迭代类型。对于 Python 中所有会从左至右扫描对象的迭代工具而言都是如此,这些迭代工具包括了 for 循环、列表解析、in 成员关系测试以及 map() 内置函数等,他们都支持任何的可迭代类型。
”可迭代对象“ 的概念在 Python 中基本上和序列概念是一样的:如果对象是实际保存的序列,或者可以在迭代工具环境中(例如,for循环)一次产生一个结果的对象,就看做这个对象是可迭代的。总之,可迭代对象包括实际序列和按照需求而计算的虚拟序列(比如:range对象 和 生成器函数)。
1、迭代协议: next()
文件对象有一个方法 __next__() ,每次调用时,就会返回文件的下一行,到达文件末尾时, __next__() 会引发内置的 StopInteration 异常。
这个接口就是Python中的迭代协议:有一个 __next__() 方法的对象在迭代工具的依次迭代中会依次的前进到下一个结果,而在一系列结果的末尾时,则会引发StopInteration 异常。在Python中,任何这类对象都认为是可迭代的。任何这类对象也能以 for 循环或其他迭代工具遍历,因为所有的迭代工具内部工作起来都是在每次迭代中调用可迭代对象的 __next__() 方法,并且捕捉 StopInteration 异常来确定何时离开。
所以逐行读取文本文件的最佳方法就是根本不要去读取,而是让 for 循环在每次迭代中自动调用文件对象的 __next__() 方法从而前进到下一行。
这里的 print() 使用 end='' 参数来抑制自动添加一个 '\n',因为字符串已经有一个(如果 print() 再自动加上一个,我们的输出将会多输出一个空行)。
上面是读取文本文件的最佳方式,因为这是代码最简单、运行最快的方法,并且从内存使用情况来说也是最好的。迭代器在 Python 中是以 C 语言的速度运行的,而 while 循环则是通过 Python 虚拟机运行 Python 字节码的。所以我们一直建议在能够使用 for 循环的时候都使用 for 循环,因为他使用更方便,运行更高效。
2、内置函数:next() 和 iter()
为了更方便的手动迭代可迭代对象,Python 3.0 还提供了一个内置函数 next() ,它会自动调用一个对象的 __next__() 方法。也就是说对于一个可迭代对象,使用 next() 内建函数调用和调用其 __next__() 方法是一样的,但是前者要简单很多。
当 for 循环开始时,会将对象传给 iter() 内置函数,以便从可迭代对象中获得一个迭代器,返回的对象含有需要的 __next__() 方法。
对于文件对象来说是不需要调用 iter() 函数的,因为文件对象拥有自己的迭代器。也就是说,文件对象有自己的 __next__() 方法。
列表以及很多其他的内置对象,自身没有 __next__() 方法,也就是没有自身的迭代器,但是它们支持多次打开(创建)迭代器。对于这样的对象,我们必须调用 iter() 函数来为他们创建迭代器,启动迭代。
尽管Python的迭代工具自动调用这些函数,我们也可以使用它们来手动地应用迭代协议。
3、其他迭代环境
在 Python 的内置工具集中从左至右扫描一个对象的每项工具都定义为在对象上使用了迭代协议。列表解析、in 成员关系测试、map() 内置函数以及像 sorted() 和 zip() 调用这样的内置函数也都使用了迭代协议,所有这些都接收一个可迭代的对象。
Python还包含了各种处理迭代的其他内置函数:
sorted() 排序,可迭代对象中的各项;
zip() 组合,可迭代对象中的各项;
enumerate() 根据相对位置来配对,可迭代对象中的各项,
filter() 选择一个函数为真的项;
reduce() 针对可迭代对象中的成对的项运行一个函数。
二、Python 3.0 中的可迭代对象
Python 3.0 中的一个基本改变是,它更强调迭代。除了与文件和序列这样的内置类型相关的迭代,字典本身,以及字典的方法 keys(),values() 和 items() 都在 Python 3.0 中返回可迭代对象,就像内置函数 range()、map()、zip() 和 filter() 所做的那样。
1、字典
在Python3.0 中,字典对象也支持创建自己的迭代器,在迭代环境中,会自动一次返回一个键。
字典的 keys() 、values() 和 items() 方法返回可迭代的视图对象,没有自己的迭代器,它们一次产生一个结果项,而不是在内存中一次产生全部结果列表。
注意: 在 Python 3.0 中字典对象是可迭代的(没有自己的迭代器),它返回连续的键。因此无需直接在此环境中调用 keys() 方法,而且这样由于中间生成了dict_keys对象,还会导致执行效率的下降。
2、range() 内置函数
它返回一个可迭代的 range 类型对象,range 对象根据需要产生范围中的数字,而不是在内存中构建一个结果列表。
在 Python 3.0 中 range 对象只支持迭代、索引以及 len() 函数。他不支持任何其他的序列操作。
3、map()、zip() 和 filter() 内置函数
和 range() 类似,map()、zip() 和 filter() 内置函数在 Python 3.0 中也返回可迭代对象以节约内存空间,而不在内存中一次性生成一个结果列表。
和 range() 不同,它们都拥有自己的迭代器——在遍历其结果一次以后,它们就用尽了。换句话说,不能在它们的结果上拥有那些在结果中保持不同位置的多个迭代器。
4、多个迭代器 VS 单个迭代器
range 对象没有自己的迭代器(手动迭代时,我们使用 iter() ,为 range 对象生成一个迭代器),并且它支持在其结果上创建多个迭代器,这些迭代器会记住它们各自的位置。
相反 zip 对象、map 对象 和 filter 对象拥有自己的迭代器(可以直接调用next() 函数进行迭代,不用手动调用 iter() 函数来再次生成迭代器),不支持相同结果上的多个活跃迭代器。