簡體   English   中英

如何將元組的Python生成器拆分為2個獨立的生成器?

[英]How to split a Python generator of tuples into 2 separate generators?

我有一個大致如下的發電機:

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?

你不能,不能沒有最終保持所有發生器輸出只是為了能夠在第二個循環中產生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產生的所有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

我有一個可能不完全符合你想要的解決方案。 它將n元組生成器分成n單獨生成器的元組。 但是,它需要返回當前元組的每個單獨值以進入下一個元組。 嚴格地說,它將n元組生成器“拆分”為n 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只有在產生當前元組的所有值后才會進行。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM