我的生成器大致如下:
def gen1():
for x, y in enumerate(xrange(20)):
a = 5*x
b = 10*y
yield a, b
从这台发电机,我想创建两个独立的发电机,如下所示:
for a in gen1_split_a():
yield a
for b in gen1_split_b():
yield b
我的戏是什么,SA?
答案 0 :(得分:9)
你不能,不能最终保持所有发生器输出只是为了能够在第二个循环中产生b
值。在内存方面,这可能会花费很多。
您使用itertools.tee()
来“复制”生成器:
from itertools import tee
def split_gen(gen):
gen_a, gen_b = tee(gen, 2)
return (a for a, b in gen_a), (b for a, b in gen_b)
gen1_split_a, gen1_split_b = split_gen(gen1)
for a in gen1_split_a:
print a
for b in gen1_split_b:
print b
但在这种情况下会发生的情况是tee
对象最终必须存储所有gen1
生成的。来自文档:
此itertool可能需要大量辅助存储(取决于需要存储多少临时数据)。通常,如果一个迭代器在另一个迭代器启动之前使用了大部分或全部数据,则使用
list()
代替tee()
会更快。
遵循该建议,只需将b
值放入第二个循环的列表中:
b_values = []
for a, b in gen1():
print a
b_values.append(a)
for b in b_values:
print b
或更好,只需在一个循环中处理a
和b
。
答案 1 :(得分:0)
我有一个解决方案可能并不完全是您想要的。它将n
元组生成器分离为n
个独立生成器的元组。 但是,它要求返回当前元组的每个单独的值才能继续进行下一个元组。严格来说,它会将n
元组生成器“拆分”为n
生成器,但是您的示例无法按所示进行。
它利用Python将值发送回生成器以影响未来收益的能力。同样的想法也应该可以通过类来实现,但是无论如何我都想抓住生成器。
初始化新生成器时,它们仅知道当前的n
元组。每次他们在各自的索引处产生值时,都会执行回调,以将该索引通知上级生成器。产生了当前元组的所有索引后,更高级别的生成器将移至下一个元组,然后重复该过程。
可能有点笨拙,但这是代码(Python 3.6)。
from typing import TypeVar, Generator, Tuple, Iterator, Optional
TYPE_A = TypeVar("TYPE_A")
def _next_value(source: Iterator[Tuple[TYPE_A, ...]], size: int) -> Generator[Tuple[TYPE_A, ...], Optional[int], None]:
checked = [False for _ in range(size)]
value = next(source)
while True:
index = yield value
if all(checked):
value = next(source)
for _i in range(len(checked)):
checked[_i] = False
checked[index] = True
def _sub_iterator(index: int, callback: Generator[Tuple[TYPE_A, ...], int, None]) -> Generator[TYPE_A, None, None]:
while True:
value = callback.send(index)
yield value[index]
def split_iterator(source: Iterator[Tuple[TYPE_A, ...]], size: int) -> Tuple[Generator[TYPE_A, Optional[TYPE_A], None], ...]:
generators = []
_cb = _next_value(source, size)
_cb.send(None)
for _i in range(size):
each_generator = _sub_iterator(_i, _cb)
generators.append(each_generator)
return tuple(generators)
if __name__ == "__main__":
def triple():
_i = 0
while True:
yield tuple(range(_i, _i + 3))
_i += 1
g = triple()
for i, each_value in enumerate(g):
if i >= 5:
break
print(each_value)
print()
g = triple()
a_gen, b_gen, c_gen = split_iterator(g, 3)
for i, (a_value, b_value, c_value) in enumerate(zip(a_gen, b_gen, c_gen)):
if i >= 5:
break
print((a_value, b_value, c_value))
triple()
是一个三元组生成器,split_iterator()
生成三个生成器,每个生成器从triple()
生成的元组中产生一个索引。每个_sub_iterator
仅在获得了当前元组的所有值后才进行处理。