Как объединить несколько блокирующих генераторов Python в один? - PullRequest
7 голосов
/ 08 февраля 2012

Рассмотрим следующий псевдокод:

def g_user():
    while True:
        yield read_user_input()

def g_socket():
    while True:
        yield read_socket_input()

def g_combined(gu, gs):
    # should read user or socket input, whichever is available

    while True:
        sel = select(gu, gs)
        if sel.contains(gu):
            yield gu.next()
        if sel.contains(gs):
            yield gs.next()

gc = g_combined ( g_user(), g_socket() )

Как это реализовать проще всего?

Ответы [ 3 ]

8 голосов
/ 08 февраля 2012

Похоже, кто-то уже реализовал это: http://www.dabeaz.com/generators/genmulti.py

Отражено здесь:

import Queue, threading
def gen_multiplex(genlist):
    item_q = Queue.Queue()
    def run_one(source):
        for item in source: item_q.put(item)

    def run_all():
        thrlist = []
        for source in genlist:
            t = threading.Thread(target=run_one,args=(source,))
            t.start()
            thrlist.append(t)
        for t in thrlist: t.join()
        item_q.put(StopIteration)

    threading.Thread(target=run_all).start()
    while True:
        item = item_q.get()
        if item is StopIteration: return
        yield item
1 голос
/ 08 февраля 2012

Чтобы заставить select.select() работать должным образом, вам нужно предоставить метод fileno() в ваших генераторах, который возвращает базовый дескриптор файла.

0 голосов
/ 08 февраля 2012
Модуль

itertools из стандартной библиотеки содержит приятные функции. В этом конкретном случае функция цикла хороша.

Справка цикла говорит:

цикл (повторяемый) -> объект цикла

Возвращать элементы из повторяемого до тех пор, пока оно не будет исчерпано. затем повторять последовательность бесконечно.

Использование цикла в вашем решении:

import itertools

def g_user():
    while True:
        yield read_user_input()

def g_socket():
    while True:
        yield read_socket_input()


def g_combine(*generators):
    for generator in itertools.cycle(generators):
        yield generator.next()


g_combined =  g_combine(g_user(), g_socket())

Замечания по поводу этого кода:

  1. g_combine оканчивается первым генератором, который вызывает StopIteration

  2. g_combine принимает N генераторов

  3. Как правильно ловить исключения? Подумайте о.

...