繁体   English   中英

在多个生成器之间随机采样?

[英]Randomly sample between multiple generators?

我正在尝试随机迭代多个生成器,并通过从可用生成器列表中删除它们来跳过那些已经耗尽的生成器。 然而,CombinedGenerator 并没有像它应该的那样调用自己来切换生成器。 相反,当较小的迭代器用尽时,它会抛出一个 StopIteration。 我错过了什么?

以下作品:

gen1 = (i for i in range(0, 5, 1))
gen2 = (i for i in range(100, 200, 1))

list_of_gen = [gen1, gen2]
print(list_of_gen)

list_of_gen.remove(gen1)
print(list_of_gen)

list_of_gen.remove(gen2)
print(list_of_gen)

其中每个生成器都通过它们的引用被删除。

但在这里它没有:

import random

gen1 = (i for i in range(0, 5, 1))
gen2 = (i for i in range(100, 200, 1))

total = 105

class CombinedGenerator:
    def __init__(self, generators):
        self.generators = generators

    def __call__(self):
        generator = random.choice(self.generators)

        try:
            yield next(generator)
        except StopIteration:
            self.generators.remove(generator)
            if len(self.generators) != 0:
                self.__call__()
            else:
                raise StopIteration

c = CombinedGenerator([gen1, gen2])

for i in range(total):
    print(f"iter {i}")
    print(f"yielded {next(c())}")

正如@Tomerikoo 提到的,您基本上是在创建自己的生成器,最好实现__next__ ,这是更清洁和 pythonic 的方式。

上面的代码可以用下面的行来修复。

def __call__(self):
    generator = random.choice(self.generators)

    try:
        yield next(generator)
    except StopIteration:
        self.generators.remove(generator)
        if len(self.generators) != 0:
            # yield your self.__call__() result as well
            yield next(self.__call__())
        else:
            raise StopIteration

首先,为了修复您当前的代码,您只需要通过更改行来匹配您创建的模式:

self.__call__()

至:

yield next(self.__call__())

然后,我将对您的原始代码进行一些小改动:

  • 与其实现__call__并调用 object,不如实现__next__并简单地在 object 上调用next似乎更合理。
  • 我不会选择生成器,而是选择索引。 这主要是为了避免在您可以直接访问已删除的 object 时使用效率不高的remove
  • 就我个人而言,我更喜欢在可能的情况下避免递归,所以会改变我检查仍然要使用的生成器的位置:
class CombinedGenerator:
    def __init__(self, generators):
        self.generators = generators

    def __next__(self):
        while self.generators:
            i = random.choice(range(len(self.generators)))

            try:
                return next(self.generators[i])
            except StopIteration:
                del self.generators[i]

        raise StopIteration

c = CombinedGenerator([gen1, gen2])

for i in range(total):
    print(f"iter {i}")
    print(f"yielded {next(c)}")

一个不错的奖励是将其添加到您的 class 中:

    def __iter__(self):
        return self

然后,您可以直接在 object 本身上进行迭代,并且您不需要total变量:

for i, num in enumerate(c):
    print(f"iter {i}")
    print(f"yielded {num}")

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM