繁体   English   中英

将迭代器传递给多个使用者?

[英]pipeline an iterator to multiple consumers?

是否可以“流水线”跨越多个消费者的发电机消费?

例如,使用以下模式的代码是很常见的:

def consumer1(iterator):
    for item in iterator:
        foo(item)

def consumer2(iterator):
    for item in iterator:
        bar(item)

myiter = list(big_generator())
v1 = consumer1(myiter)
v2 = consumer2(myiter)

在这种情况下,多个函数完全消耗相同的迭代器,因此有必要将迭代器缓存在列表中。 由于每个使用者都耗尽了迭代器,因此itertools.tee毫无用处。

我经常看到这样的代码,我一直希望我可以让消费者一次消费一个项目,而不是缓存整个迭代器。 例如:

  1. consumer1消费myiter[0]
  2. consumer2消费myiter[0]
  3. consumer1消费myiter[1]
  4. consumer2消费myiter[1]
  5. 等等...

如果我要编写一个语法,它将看起来像这样:

c1_retval, c2_retval = iforkjoin(big_generator(), (consumer1, consumer2))

您可以使用线程或多处理程序和tee迭代器进行接近,但是线程消耗的速度不同,这意味着在tee缓存的双端队列值可能会变得非常大。 这里的目的不是利用并行性或加快任务,而是避免缓存迭代器的大部分。

在我看来,如果不修改使用者,这可能是不可能的,因为控制流在使用者中。 但是,当消费者实际上消费了迭代器时,控件将传递到迭代器的next()方法中,因此也许可以以某种方式反转控件的流,以便迭代器一次阻止一个消费者,直到可以将其全部供入?

如果可能的话,我不太聪明,怎么做。 有任何想法吗?

这不行吗? 还是您需要整个迭代器,这样才能像这样复制到每个迭代器? 如果是这样,那么我认为您要么必须创建一个副本,要么两次生成列表?

for item in big_generator():
    consumer1.handle_item(item)
    consumer2.handle_item(item)

由于不更改使用者代码(即在其中包含循环)的局限性,您只剩下两个选择:

  1. 您已经在问题中包含的方法:将生成的项目缓存在内存中,然后对其进行多次迭代。
  2. 在线程中运行每个使用者,并实现某种itertools.tee ,其缓冲区大小为1,该缓冲区阻止为项目i+1提供服务,直到将项目i提供给所有使用者。

没有其他选择。 您无法实现以下所有目的,因为它们是矛盾的:

  1. 有发电机
  2. 有一个循环来消耗所有
  3. 然后, 上一个循环完成之后 (串行),让另一个循环再次使用所有循环
  4. 在使用它们时仅将O(1)个项目保留在内存(或磁盘等)中
  5. 不重新生成(即不重新创建生成器)

如果要重复使用,则必须将生成的项目存储在某个位置

如果可以更改消费者的代码,那么@monkey的解决方案显然是最简单,最直接的方法。

暂无
暂无

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

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