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