迭代器探索--cookbook读书笔记

来源:互联网 发布:我的世界懒人js制作器 编辑:程序博客网 时间:2024/06/05 11:07

1. 可迭代

Python 的迭代协议要求一个__iter__() 方法返回一个特殊的迭代器对象,这个迭代器对象实现__next__() 方法并通过StopIteration 异常标识迭代的完成。

为了达到这些要求,我们有几种方法实现:

a. 直接使用iter()函数,参数为序列;

b. 通过一个带yield返回的函数实现一个生成器,生成器实现可迭代;

c. 通过类内部函数__next__()实现可迭代。

最简便的方式是使用生成器,第c种过于繁琐。

2. 反向迭代

使用内置的reversed() 函数可以反向一个序列。反向迭代仅仅当对象的大小可预先确定或者对象实现__reversed__() 的特殊方法时才能生效。如果两者都不符合,那你必须先将对象转换为一个列表才行。定义一个反向迭代器可以使得代码非常的高效,因为它不再需要将数据填充到一个列表中然后再去反向迭代这个列表。

>>> a = [1, 2, 3, 4]>>> for x in reversed(a):... print(x)...4321

3. 带有外部状态的生成器

如果你想让你的生成器暴露外部状态给用户,别忘了你可以简单的将它实现为一个类,然后把生成器函数放到__iter__() 方法中过去。使用这个类,你可以将它当做是一个普通的生成器函数。然而,由于可以创建一个实例对象,于是你可以访问内部属性值。

如果你在迭代操作时不使用for 循环语句,那么你得先调用iter() 函数。

4. 迭代器切片

函数itertools.islice() 正好适用于在迭代器和生成器上做切片操作。islice() 会消耗掉传入的迭代器中的数据。必须考虑到迭代器是不可逆的这个事实。所以如果你需要之后再次访问这个迭代器的话,那你就得先将它里面的数据放入一个列表中。

>>> def count(n):... while True:... yield n... n += 1...>>> c = count(0)>>> import itertools>>> for x in itertools.islice(c, 10, 15):... print(x)...1011121314

5. 跳过开始部分迭代

itertools.dropwhile() 函数。使用时,你给它传递一个函数对象和一个可迭代对象。它会返回一个迭代器对象,丢弃原有序列中直到函数返回True 之前的所有元素,然后返回后面所有元素。

>>> from itertools import dropwhile>>> with open('/etc/passwd') as f:... for line in dropwhile(lambda line: line.startswith('#'), f):... print(line, end='')

6. 排列组合的迭代

itertools 模块提供了三个函数来解决排列组合的迭代。

itertools.permutations() , 它接受一个集合并产生一个元组序列, 每个元组由集合中所有元素的一个可能排列组成。也就是说通过打乱集合中元素排列顺序生成一个元组。

>>> items = ['a', 'b', 'c']>>> from itertools import permutations>>> for p in permutations(items):... print(p)...('a', 'b', 'c')('a', 'c', 'b')('b', 'a', 'c')('b', 'c', 'a')('c', 'a', 'b')('c', 'b', 'a')
使用itertools.combinations() 可得到输入集合中元素的所有的组合。对于combinations() 来讲,元素的顺序已经不重要了。也就是说,组合('a','b') 跟('b', 'a') 其实是一样的
>>> from itertools import combinations>>> for c in combinations(items, 3):... print(c)...('a', 'b', 'c')>>> for c in combinations(items, 2):... print(c)...('a', 'b')('a', 'c')('b', 'c')
在计算组合的时候, 一旦元素被选取就会从候选中剔除掉(比如如果元素’a’ 已经被选取了, 那么接下来就不会再考虑它了)。而函数itertools.combinations with replacement() 允许同一个元素被选择多次。

7. 序列上索引值迭代

内置的enumerate() 函数

>>> my_list = ['a', 'b', 'c']>>> for idx, val in enumerate(my_list, 1):... print(idx, val)...1 a2 b3 c

8. 同时迭代多个序列

为了同时迭代多个序列,使用zip() 函数。

9. 不同集合上元素的迭代

itertools.chain() 方法可以用来简化这个任务。

>>> from itertools import chain>>> a = [1, 2, 3, 4]>>> b = ['x', 'y', 'z']>>> for x in chain(a, b):... print(x)...1234xyz

10. 展开嵌套的序列

可以写一个包含yield from 语句的递归生成器来展开嵌套序列。语句yield from 在你想在生成器中调用其他生成器作为子例程的时候非常有用。

from collections import Iterabledef flatten(items, ignore_types=(str, bytes)):    for x in items:    if isinstance(x, Iterable) and not isinstance(x, ignore_types):        yield from flatten(x)    else:        yield xitems = [1, 2, [3, 4, [5, 6], 7], 8]# Produces 1 2 3 4 5 6 7 8for x in flatten(items):    print(x)

11. 顺序迭代合并后的排序迭代对象

heapq.merge() 函数

>>> import heapq>>> a = [1, 4, 7, 10]>>> b = [2, 5, 6, 11]>>> for c in heapq.merge(a, b):... print(c)...1245671011

12. 迭代器代替while 无限循环

iter 函数一个鲜为人知的特性是它接受一个可选的callable 对象和一个标记(结尾) 值作为输入参数。当以这种方式使用的时候,它会创建一个迭代器,这个迭代器会不断调用callable 对象直到返回值和标记值相等为止。这种特殊的方法对于一些特定的会被重复调用的函数很有效果,比如涉及到I/O调用的函数。
>>> import sys>>> f = open('/etc/passwd')>>> for chunk in iter(lambda: f.read(10), ''):... n = sys.stdout.write(chunk)