迭代器是Python中最强有力的特性之一,从最高层次看,我们可以简单地把迭代看做是一种处理序列中元素的方式。但是这里还有更多的可能,比如创建自己的可迭代对象、在itertools模块中选择使用的迭代模式,构建生成器函数等;
1.手动访问迭代器中的元素
问题:我们需要处理某个可迭代对象中的元素,但是基于某种原因不能也不想使用for循环
解决方案:用手动访问可迭代对象中的元素,可以使用next()函数,然后自己编写代码来捕获stopIteration异常;
>>>with open('/ect/passwd') as f:
>>> try:
>>> while True:
>>> line = next(f)
>>> print(line,end='')
>>> except StopIteration:
>>> pass
一般来说,StopIteration异常时用来通知我们迭代结束的。但是如果是手动使用next(),也可以命令他返回一个结束值
- 大多数情况下,我们会用for语句来访问可迭代对象中的元素。但是偶然也会碰到需要对底层迭代机制做更精细控制的情况。
>>> items = [1,2,3]
>>> it = iter(items)
>>> next(it)
1
>>> next(it)
2
>>> next(it)
3
>>> next(it)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
2.委托迭代
问题:我们构建了一个自定义的对象容器,其内部持有一个列表,元祖或其他的可迭代对象。我们想让自己的新容器能够完成迭代操作;
解决方案:一般来说,我们所要做的就是定义一个iter()方法,将迭代请求委托到对象内部持有的容器上;
3.用生成器创建新的迭代模式
问题:我们想要实现一个自定义的迭代模式,使其区别于常见的内建函数(即range()、reversed()等)
解决方案:如果想要实现一种新的迭代模式,可使用生成器函数来定义,这里有一个生成器可产生某个范围内的浮点数;
>>> def frange(start,stop,increment):
... x = start
... while x < stop:
... yield x
... x += increment
...
>>> for n in frange(0,4,0.5):
... print(n)
...
0
0.5
1.0
1.5
2.0
2.5
3.0
3.5
- 函数中只要出现了yield语句就会将其转变成一个生成器。与普通函数不同,生成器只会在响应迭代操作时才运行。
4.实现迭代协议
问题:我们正在构建一个自定义的对象,希望他可以支持迭代操作,但是也希望有一种简单的方式来实现迭代协议;
解决方案:目前来看,要在对象上实现可迭代功能,最简单的方式就是使用生成器函数;
5.反向迭代
>>> a = [1,2,3,4,5]
>>> for x in reversed(a):
... print(x)
...
5
4
3
2
1
- 方向迭代只有在待处理的对象拥有可确定的大小,或者对象实现了reversed()特殊方法时,才能奏效。如果这两个条件都无法满足,则必须首先将这个对象转换为列表;
>>>class Countdown:
>>>
>>> def __init__(self,start):
>>> self.start = start
>>>
>>> #Forward iterator
>>> def __iter__(self):
>>> n = self.start
>>> while n > 0:
>>> yield n
>>> n -= 1
>>>
>>> #Reversed iterator
>>> def __reversed__(self):
>>> n = 1
>>> while n < self.start:
>>> yield n
>>> n += 1
6.定义带有额外状态的生成器函数
7.对迭代器作切片操作
8.跳过迭代对象中的前一部分元素
9.迭代所有可能的组合或排列
10.以索引-值对的形式迭代序列
问题:我们想迭代一个序列,但是又想记录下序列中当前处理到元素的索引
解决方案:内建的enumerate()函数可以非常漂亮的解决这个问题:
>>> my_list = ['a','b','c']
>>> for idx,val in enumerate(my_list,1):
... print(idx,val)
...
1 a
2 b
3 c
>>> for idx,val in enumerate(my_list):
... print(idx,val)
...
0 a
1 b
2 c
enumerate()可以方便地用来跟踪特定值出现在列表中的偏移位置,比如,如果想将文件中的单词和他们所出现的行之间建立映射关系,可以通过使用enumerate()来将每个单词映射到文件行相应的偏移位置来实现:
>>> from collections import defaultdict
>>> word_summary = defaultdict(list)
>>> with open('D:\\project\\test.txt','r') as f:
... lines = f.readlines()
...
>>> for idx,line in enumerate(lines):
... words = [w.strip().lower() for w in line.split() ]
... for word in words:
... word_summary[word].append(idx)
...
>>> print(word_summary)
defaultdict(<class 'list'>, {'elasticsearch': [0, 2, 4, 6, 7, 7, 8, 8], 'is': [0, 0, 5, 5], 'a': [0, 2, 6, 6, 6, 7, 8, 8], 'highly': [0], 'scalable': [0], 'open-source': [0], 'full-text': [0], 'sea
处理完文件之后,如果打印word_summary,将得到一个字典(准确的说是一个defaultdict),而且每个单词都是字典的键,每个单词键所对应的值就是由行号组成的列表,表示这个单词层出现过的所有行。如果但是在一行中出现过2次,那么这个行号就会记录2次;
11.同时迭代多个序列
我们想要迭代的元素包含在多个序列中,我们想同时对他们进行迭代;
解决方案:可以使用zip()函数来同时迭代多个序列;
>>> xpts = [1,5,4,2,10,7]
>>> ypts = ['a','b','c','d','e','f']
>>> for x,y in zip(xpts,ypts):
... print(x,y)
...
1 a
5 b
4 c
2 d
10 e
7 f
zip(a,b)的工作原理是创建一个迭代器,该迭代器可产生出元祖(x,y),这里的x取自a,y取自序列b,当其中某个输入序列没有元素可以继续迭代时,整个迭代过程结束;
>>> xpts = [1,5,4,2,10,7]
>>> ypts = ['a','b','c']
>>> for x,y in zip(xpts,ypts):
... print(x,y)
...
1 a
5 b
4 c
如果这种行为不是所需要的,可以使用itertools.zip_longest()来替代
>>> from itertools import zip_longest
>>> for i in zip_longest(xpts,ypts):
... print(i)
...
(1, 'a')
(5, 'b')
(4, 'c')
(2, None)
(10, None)
(7, None)
zip()通常用在需要将不同的数据配对在一起时。
>>> headers = ['name','shares','price']
>>> values = ['overad','100','490.1']
>>> s = dict(zip(headers,values))
>>> for name,val in zip(headers,values):
... print(name,'=',val)
...
name = overad
shares = 100
price = 490.1
>>> print(s.keys())
dict_keys(['name', 'shares', 'price'])
>>> print(s.values())
dict_values(['overad', '100', '490.1'])
最后需要重点强调的是,zip()函数创建出的结果只是一个迭代器,如果需要将配对的数据保存为列表,那么请使用list()函数;
>>> zip(xpts,ypts)
<zip object at 0x0000000003C03648>
>>> list(zip(xpts,ypts))
[(1, 'a'), (5, 'b'), (4, 'c')]
12.在不同的容器中进行迭代
问题:我们需要对许多对象执行相同的操作,但是这些对象包含在不同的容器内,而我们希望可以避免写出嵌套的循环处理,保持代码的可读性;
解决方案:itertools.chain()方法可以用来简化这个任务。他可以接受一系列可迭代对象并返回一个迭代器。这个迭代器能够有效的掩盖一个事实:你实际上实在对多个容器进行迭代;
>>> from itertools import chain
>>> a = [1,2,3,4,5]
>>> b = ['x','y','z']
>>> for x in chain(a,b):
... print(x)
...
1
2
3
4
5
x
y
z
在程序中,chain()常见的用途是想一次性对所有的元素执行某项特定的操作,但是这些元素分散在不同的集合中;
>>>active_items = set()
>>>inactive_items = set()
>>>#Iterate over all items
>>>for item in chain(active_items,inactive_items):
... #Process item
13.创建处理函数的管道
14.扁平化处理嵌套型的序列
15.合并多个有序序列,再对整个有序序列进行迭代
问题:我们有一组有序序列,想对他们合并在一起后的有序序列进行迭代;
解决方案:heapq.merge()函数正是我们所需要的。
>>> import heapq
>>> a = [1,3,6,8,9,12]
>>> b = [2,4,5,10]
>>> for c in heapq.merge(a,b):
... print(c)
...
1
2
3
4
5
6
8
9
10
12
heapq.merge()函数要求所有的序列都是有序的;