迭代器和生成器
一、手动访问迭代器中的元素
要手动访问可迭代对象中的元素,可以使用next()函数。
with open('/etc/passwd') as f: try: while True: line = next(f) print(line, end='') except StopIteration: pass with open('/etc/passwd') as f: while True: line = next(f,None) if line is None: break print(line, end='')
二、委托迭代
Python的迭代协议要求__iter__()返回一个特殊的迭代器对象,由该对象实现的__next__()方法来完成实际的迭代。
如果要做的只是迭代另一个容器中的内容,我们不必担心底层细节是如何工作的,所要做的就是转发迭代请求。
class Node: def __init__(self,value): self._value = value self._children = [] def __repr__(self): return 'Node({!r})'.format(self._value) def add_children(self,node): self._children.append(node) def __iter__(self): return iter(self._children)
__iter__()方法只是简单地将迭代请求转发给对象内部持有的_children属性上。
四、实现迭代协议
实现一个迭代器能够以深度优先的模式遍历树的节点
class Node: def __init__(self,value): self._value = value self._children = [] def __repr__(self): return 'Node({!r})'.format(self._value) def add_children(self,node): self._children.append(node) def __iter__(self): return iter(self._children) def depch_first(self): yield self for c in self: for k in c.depch_first(): yield k root = Node(0) child1 = Node(1) child2 = Node(2) root.add_children(child1) root.add_children(child2) child1.add_children(Node(3)) child1.add_children(Node(4)) child2.add_children(Node(5)) for ch in root.depch_first(): print(ch) # Node(0) Node(1) Node(3) Node(4) Node(2) Node(5)
五、定义带有额外状态的生成器函数
如果想让生成器将状态暴露给用户,别忘了可以轻易地将其实现为一个类,然后把生成器函数的代码放到__iter__()方法中即可。
from collections import deque class Linehistory: def __init__(self,lines,histlen=3): self.lines = lines self.history = deque(maxlen=histlen) def __iter__(self): for lineno,line in enumerate(self.lines,1): self.history.append((lineno,line)) yield line def clean(self): self.history.clear() with open('somefile.txt') as f: lines = Linehistory(f) for line in lines: if 'python' in line: for lineno, hline in lines.history: print('{}:{}'.format(lineno, hline),end='')
使用这个类,可以将其看做是一个普通的生成器函数,但是,由于它会创建一个类实例,所以可以访问内部属性。
十五、合并多个有序序列、再对整个有序序列进行迭代
heapq.merge() 要求所有的输入序列都是有序的。只是简单地检查每个输入序列中的第一个元素,将最小的那个发送出去。不断重复步骤直至耗尽。
>>> import heapq
>>> a = [1, 4, 7, 10]
>>> b = [2, 5, 6, 11]
>>> for c in heapq.merge(a,b):
... print(c) # 1 2 4 5 6 7 19 11
文件读取写入例子:
import heapq with open('sorted_file_1','rt') as file1,\ open('sorted_file_2','rt') as file2,\ open('merged_file','wt') as outf: for line in heapq.merge(file1,file2): outf.write(line)
十六、用迭代器取代while循环
涉及I/O处理的程序中
CHUNKSIZE = 8192 def reader(s): while True: data = s.recv(CHUNKSIZE) if data == b'' break process_data(data)
利用iter()来替换:
def reader(s): for chunk in iter(lambda :s.recv(CHUNKSIZE), b''): process_data(data)
iter()会创建一个迭代器,然后重复调用用户提供的可调用对象,直到它返回哨兵值为止。