实际编码中,我们可能会碰到这样的需求,实现一个生成器函数,但是实现之前要知道标准库中有什么可用, 否则很可能会重新发明轮子,举个栗子~

实现一个无穷等差数列,首项 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]

未完待续~