9

複数のコンシューマー間でジェネレーターの消費を「パイプライン化」することは可能ですか?

たとえば、次のパターンのコードを使用するのが一般的です。

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 つの項目を順番に消費できるようにしたいといつも思っています。例えば:

  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が、スレッドは異なる速度で消費するため、内部にキャッシュされた値の dequeteeが非常に大きくなる可能性があります。ここでのポイントは、並列処理を利用したり、タスクを高速化したりすることではなく、反復子の大きなセクションをキャッシュしないようにすることです。

制御の流れは消費者にあるため、消費者を変更しないとこれは不可能かもしれないと私には思えます。ただし、消費者が実際に反復子コントロールを消費すると、反復子のnext()メソッドに渡されるため、何らかの形で制御の流れを逆にして、反復子が消費者を一度に 1 つずつブロックして、すべてをフィードできるようにすることは可能でしょうか?

これが可能である場合、私はその方法を理解できるほど頭が良くありません。何か案は?

4

2 に答える 2

1

これはうまくいきませんか?または、イテレータ全体が必要なので、このようなそれぞれへのコピーは機能しませんか?もしそうなら、私はあなたがコピーを作成する必要があると思います、そうでなければリストを2回生成しますか?

for item in big_generator():
    consumer1.handle_item(item)
    consumer2.handle_item(item)
于 2013-03-24T03:57:54.533 に答える
1

コンシューマーのコードを変更しないという制限 (つまり、それらにループがある) があるため、残された選択肢は 2 つだけです。

  1. 質問に既に含まれているアプローチ:生成されたアイテムをメモリにキャッシュし、それらを複数回反復します。
  2. itertools.tee各コンシューマーをスレッドで実行し、サイズ = 1 のバッファーを使用して、アイテムがすべてのコンシューマーに提供さi+1れるまでアイテムの提供をブロックする、ある種の synchronized- を実装します。i

他のオプションはありません。矛盾しているため、以下のすべてを達成することはできません。

  1. 発電機を持っている
  2. すべてを消費するループを持つ
  3. 次に、(連続して)前のループが終了した後、別のループですべてを再度消費します
  4. O(1) 個のアイテムのみをメモリ (またはディスクなど) に保持し、それらを消費する
  5. 再生成しない (つまり、ジェネレーターを再作成しない)

生成されたアイテムを再利用する場合は、どこかに保存する必要があります。

消費者のコードの変更が許容される場合、明らかに @monkey のソリューションが最も単純で簡単です。

于 2013-03-24T06:39:43.397 に答える