迭代器:初探
for循环可以用于Python中任何序列类型,包括列表,元组以及字符串。实际上,for循环可以用于任何可迭代的对象,在Python中所有会从左至右扫描对象的迭代工具都是如此,这些迭代工具包括了,for循环,列表解析,in关系测试以及内置map函数等等。
“可迭代对象”在Python中是一个相当新颖的设计,实际上可迭代就是序列观念的通用化:如果对象是可迭代的,那么再一些迭代工具中,每一次迭代都会返回一个结果对象,这就是可迭代的。
可迭代对象
在Python中任何可迭代的对象都拥有一个方法:__next__方法,该方法会“前进返回”下一个对象结果,而在一系列对象的结尾,返回一个StopIteration异常。任何拥有__next__方法的对象都是认为可迭代的,在迭代工具进行遍历时,都是Python内部对相应对象__next__方法的调用,并捕捉StopIteration异常来停止迭代。
对于一些Python内置对象来说,对象自身并不是迭代器,自身并不包含next方法,他们支持多次打开迭代器。从具体的技术上讲,在迭代工具开始迭代之前,会调用对象的iter函数(可以多次打开迭代器,实现重头开始迭代的效果),该函数返回可迭代对象的迭代器,返回的对象中才会包含需要的next方法。
>>> l
[1, 2, 3, 4]
>>> l1=l.__iter__() #l对象中是不存在__next__函数的,只有通过iter函数返回的对象才含有next函数。
>>> l1.__next__()
1
>>> l1.__next__()
2
>>> l1.__next__()
3
>>> l1.__next__()
4
>>> l1.__next__()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>> l2=iter(l) #也可以使用内置的iter函数达到相同的效果。
>>> l2
<list_iterator object at 0x7f8f1941d4e0>
>>> l2.__ne
l2.__ne__( l2.__new__( l2.__next__(
>>> l2.__next__()
1
>>> l2.__next__()
2
>>> l2.__next__()
3
>>> l2.__next__()
4
>>> l2.__next__()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>
字典的迭代器在迭代环境中,会自动一次返回一个键。直接效果是,我们不再需要调用keys方法来遍历键值。
>>> D={'a':1,'b':2,'c':3} >> D {'c': 3, 'b': 2, 'a': 1} >> for i in D: #迭代器每一次会返回一个键 ... print(i) ... c b a >>
迭代协议也用于list函数,以产生一个列表。
>>> range(5)
range(0, 5)
>>> list(range(5))
[0, 1, 2, 3, 4]
>>>
列表解析
列表解析中也存在迭代器:
>>> [i**2 for i in range(5)]
[0, 1, 4, 9, 16]
>>>
列表解析已[]方括号开始,这声明了返回的对象是一个列表,在方括号中存在着一个“反向”的for循环,迭代工具for将迭代器range(5)中的每一个元素依次取出,做相应的处理(i**2),从而返回一个包含每次迭代结果的列表。列表解析比传统的for循环语句执行速度更快,书写起来更简洁,在Python代码中非常常见。
列表解析也常常用于读取文件:
当你想在读取文件内容的同时,去掉每一行末尾的换行符时,你可以使用下面这个高效的列表解析方式:
>>> [line.rstrip() for line in open('filePython.txt')] #处理方法可以举一反三,换成其它字符串对象的方法
['line1 one', 'line2 two', 'line3 three', '123456', 'abcdefg', '']
Python扫描了文件,并自动构建了一个列表,这是一个很高效的操作,因为大多数工作都是在Python解释器内部完成,这比同效语句有更高的效率。特别针对大文件,列表解析的速度有更加明显的效率。
列表解析的扩展
实际上,列表解析还可以有其它更复杂的嵌套或扩展。
#列表中加入判断,如果不满足判断,则该行字符串将不会读入列表中
>>> [line.rstrip() for line in open('filePython.txt') if "line" in line ]
['line1 one', 'line2 two', 'line3 three']
#读入列表后,再进行删选判断,替换为其它值。
>>> [line.rstrip() if "line" in line else "HAHAHA!" for line in open('filePython.txt') ]
['line1 one', 'line2 two', 'line3 three', 'HAHAHA!', 'HAHAHA!', 'HAHAHA!']
#两个for循环的嵌套,组合字符串的功能
>>> [x+y for x in 'abc' for y in 'xyz']
['ax', 'ay', 'az', 'bx', 'by', 'bz', 'cx', 'cy', 'cz']
>>>
其它迭代环境及注意要点
- 在对象中从左到右扫描的每种工具都使用了迭代协议。
- 列表解析、in成员测试、map函数、以及sort,zip内置函数都使用了迭代协议
- Python还包含了各种处理迭代的其它内置函数:enumerate根据相对位置来配对可迭代对象中的项。filter函数(会返回参数函数返回值为True的每一项),reduce针对可迭代对象中的成对的项运行一个函数。
- 实际上,可迭代性在Python中存在很普遍,有很多看似跟迭代没关系的函数或方法,都在Python内部或多或少地涉及到了可迭代操作。
- 可迭代对象通过iter函数可以返回一个新的对象从而支持多个迭代器,但有的对象(比如zip,filter和map返回的对象)就不能支持相同结果上的多个迭代器