如何将元组的Python生成器拆分为2个独立的生成器?

问题描述 投票:11回答:2

我有一个大致如下的发电机:

def gen1():
    for x, y in enumerate(xrange(20)):
        a = 5*x
        b = 10*y
        yield a, b

从这个生成器,我想创建2个单独的生成器,如下所示:

for a in gen1_split_a():
    yield a

for b in gen1_split_b():
    yield b

什么是我的游戏,SA?

python generator
2个回答
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

或者更好的是,只需在一个循环中处理ab


0
投票

我有一个可能不完全符合你想要的解决方案。它将n-tuple发生器分离为n个体发生器的元组。但是,它需要返回当前元组的每个单独值以进入下一个元组。严格来说,它将n-tuple生成器“拆分”为n生成器,但是您的示例将无法正常工作。

它利用了Python将值发送回生成器以影响未来收益的能力。同样的想法也应该可以通过类来实现,但我想要掌握生成器。

初始化新生成器时,它们只知道当前的n-tuple。每次它们在各自的索引处产生值时,都会执行回调,以通知更高级别的生成器此索引。一旦产生了当前元组的所有索引,则更高级别的生成器移动到下一个元组并重复该过程。

它可能有点笨拙,但这里是代码(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()是一个3元组生成器,split_iterator()生成三个生成器,每个生成器从triple()产生的元组产生一个索引。每个_sub_iterator只有在产生当前元组的所有值后才会进展。

© www.soinside.com 2019 - 2024. All rights reserved.