文章目录
-
手动访问迭代器元素
- 委托迭代
- 用生成器创建新的迭代模式
- 实现迭代协议
- 反向迭代
-
自定义反向迭代器
- 定义带有额外状态的生成器函数
手动访问迭代器元素
需求:
需要处理可迭代对象的元素,但是不想或不能使用for循环。
例子:手工从文件读取文本行
1
2
3
4
5
6
7
8
9 1with open('/etc/passwd') as f:
2 try:
3 while True:
4 line = next(f)
5 print(line,end='')
6 except StopIteration:
7 pass
8
9
注意:
- 读取文文件时使用上下文管理器可以省去对文件是否读取成功的验证
- 使用next函数访问可迭代对象元素
- 使用StopIteration判断是否迭代结束
熟悉c/c++的话可以换一种判断迭代结束的方式:
1
2
3
4
5
6
7
8 1with open('/etc/passwd') as f:
2 while True:
3 line = next(f)
4 if line is None:
5 break
6 print(f,end='')
7
8
委托迭代
需求:
我们构建了一个自定义的容器对象,其内部有一个列表、元组或其他的可迭代对象,我们希望让自己的新容器能够完成迭代操作。
方法:
定义一个__iter__()方法,将迭代请求委托到对象内部持有的容器上:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 1class Node:
2 def __init__(self, value):
3 self._value = value
4 self._children = []
5
6 def __repr__(self):
7 return 'Node({!r})'.format(self._value)
8
9 def add_child(self,node):
10 self._children.append(node)
11
12 def __iter__(self):
13 return iter(self._children)
14
15# Example
16if __name__ == '__main__':
17 root = Node(0)
18 child1 = Node(1)
19 child2 = Node(2)
20 root.add_child(child1)
21 root.add_child(child2)
22 for ch in root:
23 print(ch)
24
25
iter(s)通过调用s.iter()来实现简单地返回底层的迭代器,所以一句话就实现了转发迭代请求的功能。这和len(s)调用s.len()的方式一样。
用生成器创建新的迭代模式
需求:
我们想实现一个自定义的迭代模式,使其区别于常见的内建函数(即range()、reversed()等)。
思路:
使用生成器函数来实现。
例子:
生成某个范围内依次递增的浮点数:
1
2
3
4
5
6
7 1def frange(start,stop,increment):
2 x = start
3 while x < stop:
4 yield x
5 x += increment
6
7
要使用生成器函数,可以使用for循环对其迭代,或者通过其他可以访问可迭代对象中元素的函数(例如sum()、list()等)来使用:
1
2
3
4 1for n in frange(0,4,0.5): # for循环
2 print(n)
3
4
1
2
3 1list(frange(0,4,0.5))
2
3
函数中只要出现了yield语句就会将其转变为一个生成器。与普通函数不同,生成器只会在响应迭代操作时才运行,一旦生成器函数返回,迭代停止。
for循环语句将细节都屏蔽了,用起来很方便。
实现迭代协议
需求:
构建一个支持迭代的自定义对象
例子:
Node类表示树结构,对其深度优先遍历
方法1:利用生成器函数实现迭代协议
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 1class Node:
2 def __init__(self,value):
3 self._value = value
4 self._children = []
5
6 def __repr__(self):
7 return 'Node({!r})'.format(self._value)
8
9 def add_child(self,node):
10 self._children.append(node)
11
12 def __iter__(self):
13 return iter(self._children)
14
15 def depth_first(self):
16 yield self
17 for c in self:
18 yield from c.depth_first()
19
20# Example
21if __name__ == '__main__':
22 root = Node(0)
23 child1 = Node(1)
24 child2 = Node(2)
25 root.add_child(child1)
26 root.add_child(child2)
27 child1.add_child(Node(3))
28 child1.add_child(Node(4))
29 child2.add_child(Node(5))
30
31 for ch in root.depth_first():
32 print(ch)
33
34
depth_first()的实现很容易阅读。它首先产生自身,然后迭代每个子节点,利用子节点的depth_first()方法产生出其他元素。
具有__ next__和__ iter__方法的对象即被视为迭代器,其中next方法每执行一次返回迭代的下一项,而iter方法可以把对象变成迭代器。
执行for循环的本质即是先对对象使用iter方法, 然后不断使用next方法,直到报出StopIteration。故直接在类中定义这两个方法,从而实现迭代器协议。实现这样的对象常常比较繁琐。
方法2:直接在类中定义这两个方法,从而实现迭代器协议(单步调试一遍更容易理解,代码远比看起来复杂)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55 1class Node:
2 def __init__(self,value):
3 self._value=value
4 self._children = []
5
6 def __repr__(self):
7 return 'Node({!r})'.format(self._value)
8
9 def add_child(self,node):
10 self._children.append(node)
11
12 def __iter__(self):
13 return iter(self._children)
14
15 def depth_first(self):
16 return DepthFirstIterator(self) # 把自己传给深度优先迭代器
17
18# Depth-first traversal
19class DepthFirstIterator(object):
20
21 def __init__(self,start_node):
22 self._node = start_node # self._node存储开始节点,即根节点
23 self._children_iter = None
24 self._child_iter = None
25
26 def __iter__(self):
27 return self # DepthFirstIterator已经是迭代器了,所以返回自己本身
28
29 def __next__(self):
30 ## 刚开始的时候
31 # 为以开始节点为根的树创建一个迭代器,并赋值给self._children_iter
32 # 返回开始节点
33 if self._children_iter is None:
34 self._children_iter = iter(self._node)
35 return self._node
36
37 ## 不断迭代孩子的孩子
38 # 如果存在,返回孩子的孩子
39 # 如果没有,self._child_iter置为空,重新调用__next__
40 #If processing a child, return its next item
41 elif self._child_iter:
42 try:
43 nextchild = next(self._child_iter)
44 return nextchild
45 except StopIteration:
46 self._child_iter = None
47 return next(self)
48
49 #前进到下一个孩子重新开始深度优先,重新调用__next__
50 #Advance to the next child and start its iteration
51 else:
52 self._child_iter = next(self._children_iter).depth_first()
53 return next(self)
54
55
测试代码相同,不再重复,结果也相同。
方法一显然比方法二简单的多,而且容易理解。
反向迭代
需求:
想要反向迭代序列中的元素。
方法:
使用内建的reversed()函数实现反向迭代:
1
2
3
4
5 1a = [1,2,3,4]
2for x in reversed(a):
3 print(x)
4
5
必须满足以下条件之一才能进行反向迭代:
- 待处理对象拥有可确定的大小
- 对象实现了__reverse__()方法
否则需要先将可迭代对象转换为列表才能使用反向迭代,但这样做的弊端是消耗大量的内存。
自定义反向迭代器
实现一个__reverse__()方法就可以了:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 1class Countdown:
2 def __init__(self,start):
3 self.start = start
4
5 # Forward iterator
6 def __iter__(self):
7 n = self.start
8 while n > 0:
9 yield n
10 n -= 1
11
12 # Reverse iterator
13 def __reversed__(self):
14 n = 1
15 while n <= self.start:
16 yield n
17 n += 1
18
19
定义带有额外状态的生成器函数
需求:
我们想要定义一个生成器函数,但是它还涉及一些额外的状态,我们希望能以某种形式将这些状态暴露给用户。
方法:
将用户定义成类,然后把生成器函数的代码放到__iter__()方法中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 1from collections import deque
2
3class linehistory:
4 def __init__(self,lines,histlen=3):
5 self.lines = lines
6 self.history = deque(maxlen=histlen)
7
8 def __iter__(self):
9 for lineno,line in enumerate(self.lines,1):
10 self.history.append((lineno,line))
11 yield line
12
13 def clear(self):
14 self.history.clear()
15
16# Example
17if __name__ == '__main__':
18 with open('file.txt') as f:
19 lines = linehistory(f)
20 for line in lines:
21 if 'python' in line:
22 for lineno,hline in lines.history:
23 print('{}:{}'.format(lineno,hline),end='')
24
25
上述方式如果不使用for循环,而是手动调用next()访问,需要先运行一次iter(),将对象变为迭代器对象:
1
2
3
4
5
6
7
8
9
10
11
12
13 1# 只展示调用代码
2f = open('file.txt')
3lines = linehistory(f)
4for line in lines:
5 if 'python' in line:
6 try:
7 while True:
8 hline = next(lines)
9 print('{}'.format(hline), end='')
10 except StopIteration:
11 pass
12
13
1
2
3
4
5
6
7
8
9
10
11
12
13
14 1# 只展示调用代码
2f = open('file.txt')
3lines = linehistory(f)
4it = iter(lines)
5for line in lines:
6 if 'python' in line:
7 try:
8 while True:
9 hline = next(it)
10 print('{}'.format(hline), end='')
11 except StopIteration:
12 pass
13
14