实际编码中,我们可能会碰到这样的需求,实现一个生成器函数,但是实现之前要知道标准库中有什么可用, 否则很可能会重新发明轮子,举个栗子~
实现一个无穷等差数列,首项 start = 0, 公差 step = 2, 很显然你不能用列表的形式实现,不然肯定会内存溢出,你可以直接写一个生成器,但是,为什么不用内置的呢
import itertools
gen = itertools.count(start, step) # 此时 gen 是一个生成器
for _ in range(100): # 比如我们想打印前 100 个的值
print(next(gen))
Python 3 中的 itertools 模块提供了多个生成器函数, 结合使用能实现很多用法。
上面所使用的 itertools.count 函数返回的生成器能生成多个数。 如果不传
入参数, itertools.count 函数会生成从零开始的整数数列。 不过,
我们可以提供可选的 start 和 step 值;
说到多个生成器结合使用,我们对上面的例子加个条件,生成的等差数列的最大值不超过 100,很显然 itertools.count 无法做到,此时我们可以用到 itertools 下面的另一个生成器函数 itertools.takewhile()
import itertools
gen = itertools.takewhile(lambda n: n < 100, itertools.count(0, 2))
for value in gen:
print(value)
itertools.takewhile 函数会生成一个使用另一个生成器的生成器, 在指定的条件计算结果为 False 时停止
事实上 Python 标准库提供了很多生成器, 有用于逐行迭代纯文本文件的对象, 还有出色的 os.walk 函数。这个函数在遍历目录树的过程中产出文件名, 因此递归搜索文件系统像for 循环那样简单。另外有些在 itertools 和 functools 模块中,下面按类别列举吧
1、用于过滤的生成器函数
模块 | 函数 | 说明 |
---|---|---|
itertools | compress(it,selector_it) | 并行处理两个可迭代的对象; 如果 selector_it中的元素是真值, 产出 it 中对应的元素 |
itertools | dropwhile(predicate,it) | 处理 it, 跳过 predicate 的计算结果为真值的元素, 然后产出剩下的各个元素(不再进一步检查) |
(内置) | filter(predicate, it) | 把 it 中的各个元素传给 predicate,如果predicate(item) 返回真值, 那么产出对应的元素; 如果 predicate 是None, 那么只产出真值元素 |
itertools | filterfalse(predicate,it) | 与 filter 函数的作用类似, 不过 predicate 的逻辑是相反的: predicate 返回假值时产出对应的元素 |
itertools | islice(it, stop) 或islice(it, start,stop, step=1) | 产出 it 的切片, 作用类似于 s[:stop] 或s[start:stop:step], 不过 it 可以是任何可迭代的对象, 而且这个函数实现的是惰性操作 |
itertools | takewhile(predicate,it) | predicate 返回真值时产出对应的元素, 然后立即停止, 不再继续检查 |
下面在控制台演示各个函数的用法
>>> def vowel(c):
... return c.lower() in 'aeiou'
...
>>> list(filter(vowel, 'Aardvark'))
['A', 'a', 'a']
>>> import itertools
>>> list(itertools.filterfalse(vowel, 'Aardvark'))
['r', 'd', 'v', 'r', 'k']
>>> list(itertools.dropwhile(vowel, 'Aardvark'))
['r', 'd', 'v', 'a', 'r', 'k']
>>> list(itertools.takewhile(vowel, 'Aardvark'))
['A', 'a']
>>> list(itertools.compress('Aardvark', (1,0,1,1,0,1)))
['A', 'r', 'd', 'a']
>>> list(itertools.islice('Aardvark', 4))
['A', 'a', 'r', 'd']
>>> list(itertools.islice('Aardvark', 4, 7))
['v', 'a', 'r']
>>> list(itertools.islice('Aardvark', 1, 7, 2))
['a', 'd', 'a']
2、用于映射的生成器函数
模块 | 函数 | 说明 |
---|---|---|
itertools | accumulate(it, [func]) | 产出累计的总和;如果提供了 func,那么把前两个元素传给它,然后把计算结果和下一个元素传给它,以此类推,最后产出结果 |
(内置) | enumerate(iterable, start=0) | 产出由两个元素组成的元祖,结构是 (index, item),其中index 从 start 开始计数,item 则从 iterable 中获取 |
(内置) | map(func, it1, [it2, ..., itN]) | 把 it 中的各个元素传给 func,产出结果;如果传入 N 个可迭代的对象,那么 func 必须能接受 N 个参数,而且要并行处理各个可迭代的对象 |
itertools | starmap(func, it) | 把 it 中的各个元素传给 func,产出结果;输入的可迭代对象应该产出可迭代的元素 iit,然后以 func(*iit) 这种形式调用 func |
下面先单独演示 accumulate(it, [func]) 函数的用法吧,这个用法比较灵活
>>> sample = [5, 4, 2, 8, 7, 6, 3, 0, 9, 1]
>>> import itertools
>>> list(itertools.accumulate(sample)) # 计算总和
[5, 9, 11, 19, 26, 32, 35, 35, 44, 45]
>>> list(itertools.accumulate(sample, min)) # 计算最小值
[5, 4, 2, 2, 2, 2, 2, 0, 0, 0]
>>> list(itertools.accumulate(sample, max)) # 计算最大值
[5, 5, 5, 8, 8, 8, 8, 8, 9, 9]
>>> import operator
>>> list(itertools.accumulate(sample, operator.mul)) # 计算乘积
[5, 20, 40, 320, 2240, 13440, 40320, 0, 0, 0]
>>> list(itertools.accumulate(range(1, 10), operator.mul)) # 从1! 到 10!, 计算各个数的阶乘
[1, 2, 6, 24, 120, 720, 5040, 40320, 362880]
接下来演示其他的映射生成器函数
>>> list(enumerate('albatroz', 1)) # 从 1 开始, 为单词中的字母编号
[(1, 'a'), (2, 'l'), (3, 'b'), (4, 'a'), (5, 't'), (6, 'r'), (7, 'o'), (8, 'z')]
>>> list(map(operator.mul, range(11), range(11))) # 从 0 到 10, 计算各个整数的平方
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
>>> list(map(operator.mul, range(11), [2, 4, 8])) # 计算两个可迭代对象对应位置上的元素之积, 元素最少的那个可迭代对象到头后就停止了
[0, 4, 16]
>>> list(map(lambda a, b: (a, b), range(11), [2, 4, 8])) # 作用等同于内置的 zip 函数
[(0, 2), (1, 4), (2, 8)]
>>> list(itertools.starmap(operator.mul, enumerate('albatroz', 1))) #从 1 开始,根据字母所在的位置, 把字母重复相应的次数
['a', 'll', 'bbb', 'aaaa', 'ttttt', 'rrrrrr', 'ooooooo', 'zzzzzzzz']
>>> list(itertools.starmap(lambda a, b: b / a, enumerate(itertools.accumulate(sample), 1))) # 计算平均值
[5.0, 4.5, 3.6666666666666665, 4.75, 5.2, 5.333333333333333, 5.0, 4.375, 4.888888888888889, 4.5]
网友评论