摄影:产品经理买单:kingname
我们知道,Python 里面的生成器只能被消费一次,例如下面的代码:
def name_generator(): for name in ['产品经理', 'kingname']: yield name def say_hello(g): print('hello 函数开始运行') for name in g: print('hello', name) print('hello 函数运行完成') def say_hi(g): print('hi函数开始运行') for name in g: print('hi', name) print('hi函数运行完成') names = name_generator() say_hello(names) say_hi(names)
运行效果如下图所示:
在 say_hello
函数里面,生成器已经被完整遍历了一次,那么在say_hi
里面,就什么数据都拿不到了。
但如果我们用的是列表,就可以多次遍历,如下图所示:
大家注意观察区别。
那么有什么办法,能让生成器被多次完整迭代呢?这个时候就要使用itertools.tee
这个函数了。它通过dequeue
实现了让生成器多次消费的办法。
itertools.tee
的使用方法如下:
生成器1, 生成器2, 生成器3 = itertools.tee(原始生成器, 3)
itertools.tee
的第一个参数是原始生成器,第二个参数是你希望让它返回多少个可以复用的生成器。
例如:
import itertools def name_generator(): for name in ['产品经理', 'kingname']: yield name def say_hello(g): print('hello 函数开始运行') for name in g: print('hello', name) print('hello 函数运行完成') def say_hi(g): print('hi函数开始运行') for name in g: print('hi', name) print('hi函数运行完成') names = name_generator() names_1, names_2 = itertools.tee(names, 2) say_hello(names_1) say_hi(names_2)
运行效果如下图所示:
但是,itertools.tee
有两个缺陷:
其一是如果原始生成器能循环非常多次,产生的数据量非常大,并且你在消费的时候,是先迭代第一个分裂后的生成器,完整迭代完以后再迭代第二个分裂后的生成器,那么这将会浪费大量内存。所以,应该让两个生成器能间隔着迭代,或者“同时”迭代。
其二,多个生成器同时迭代也有问题,分裂出来的多个生成器不是线程安全的,在多线程里面同时运行会导致报错。
在接下来的两篇文章中,我会讲到itertools.tee
是如何做到让生成器多次迭代的,然后讲到如何让分裂以后的生成器线程安全。