Получение значения из сопрограммы в Python, а также преобразование обратного вызова в генератор

Я новичок в Python и функциональном программировании. Я использую версию 2.7.6

Я использую инфраструктуру Tornado для создания асинхронных сетевых запросов. Из того, что я узнал о функциональном программировании, я хочу, чтобы мои данные передавались через мой код с помощью генераторов. Я сделал большую часть того, что мне нужно, используя генераторы и преобразовывая данные по мере их потока через мои вызовы функций.

В самом конце моего потока я хочу сделать запрос REST для некоторых данных. У меня есть один для цикла, прежде чем я отправлю свои данные в Tornado, чтобы инициировать pull, а затем отправить HTTP-запрос. Объект http, предоставляемый Tornado, выполняет функцию обратного вызова в качестве опции и всегда возвращает будущее, которое на самом деле является объектом будущего Tornado, а не официальным Python Future.

Моя проблема в том, что, поскольку я теперь использую генераторы для вывода моих данных через свой код, я больше не хочу использовать функцию обратного вызова. Мое рассуждение заключается в том, что после того, как я верну свои данные из обратного вызова, мои данные теперь проталкиваются через мой код, и я больше не могу использовать генераторы.

Моя цель – создать интерфейс, который выглядит так:

urls = (...generated urls...) responses = fetch(urls) 

Где ответы являются генераторами по завершенным URL.

То, что я попытался сделать – среди многих вещей – преобразовать результаты обратного вызова в генератор. Я думал о чем-то подобном, хотя я далек от его реализации по другим вопросам, которые я скоро объясню. Однако я хотел, чтобы моя функция fetch выглядела примерно так:

 def fetch(urls): def url_generator(): while True: val = yield yield val @curry def handler(gen, response): gen.send(response) gen = url_generator() for u in urls: http.fetch(u, callback=handler(gen)) return gen 

Я упростил код и синтаксис, чтобы сосредоточиться на проблеме, но я решил, что это будет работать нормально. Моя стратегия заключалась в том, чтобы определить сопроцессор / генератор, после чего я отправлю ответы, когда я их получу.

У меня больше всего проблем с сопрограммой / генератором. Даже если я определяю генератор указанным выше образом и выполняю следующее, я получаю бесконечный цикл – это одна из моих основных проблем.

 def gen(): while True: val = yield print 'val', val yield val print 'after', val break g = gen() g.send(None) g.send(10) for e in g: print e 

Это печатает значение val 10 after 10 в сопрограмме coroutine, как и ожидалось с разрывом, но for-loop никогда не получает значение 10. Оно ничего не печатает во время перерыва. Если я удалю разрыв, то получаю бесконечный цикл:

 val None None after None None val None None after None None ... 

Если я удалю for-loop, то сопрограмма будет печатать только val 10 поскольку она ждет второго выхода. Я ожидаю этого. Однако использование его ничего не производит.

Аналогично, если я удаляю for-loop и заменяю его print next(g) , тогда я получаю ошибку StopIteration, которая, как я полагаю, означает, что я вызывал следующий на генераторе, у которого не было больше значений.

Anywho, я нахожусь в полной потере, пока я погружаюсь в более глубокую глубину на Python. Я полагаю, что это такая распространенная ситуация на Python, что кто-то знает отличный подход. Я искал «конвертировать обратный вызов в генератор» и т. Д., Но не имел большой удачи.

На другой ноте я мог бы дать каждое будущее из запроса http, но мне не очень повезло «ждать» на урожайности для будущего. Я много читал о «урожайности», но, похоже, это Python 3, и Tornado, похоже, пока не работает на Python 3.

Спасибо за просмотр и спасибо за любую помощь, которую вы можете предоставить.

One Solution collect form web for “Получение значения из сопрограммы в Python, а также преобразование обратного вызова в генератор”

Tornado отлично работает на Python 3.

Проблема с вашим упрощенным кодом выше заключается в том, что это не делает то, что вы ожидаете:

 val = yield 

Вы ожидаете, что генератор остановится там (блокировка вашего цикла), пока какая-то другая функция не g.send(value) , но это не то, что происходит. Вместо этого код ведет себя как:

 val = yield None 

Таким образом, for-loop получает значения None так быстро, как может их обрабатывать. После получения каждого None он неявно вызывает g.next() , который является таким же, как g.send(None) . Таким образом, ваш код эквивалентен этому:

 def gen(): while True: val = yield None print 'val', val yield val print 'after', val g = gen() g.send(None) g.send(10) while True: try: e = g.send(None) print e except StopIteration: break 

Читая эту версию кода, где неявное поведение становится явным, я надеюсь, что понятно, почему он просто генерирует None в бесконечном цикле.

То, что вам нужно, это некоторая возможность для одной функции добавлять элементы в голову очереди, а другая функция блокирует ожидание элементов и вытаскивает их из хвоста очереди, когда они будут готовы. Начиная с Tornado 4.2, мы имеем именно то, что:

http://www.tornadoweb.org/en/stable/queues.html

Пример веб-паука близок к тому, что вы хотите сделать, я уверен, что вы можете его адаптировать:

http://www.tornadoweb.org/en/stable/guide/queues.html

  • Асинхронный вызов функции с помощью Tornado Python
  • Функция отключения, не дожидаясь ответа (Python)
  • Строки Unicode в веб-приложении tornado
  • Автономный веб-сервер Python и / или nginx
  • Когда использовать Tornado, когда использовать Twisted / Cyclone / GEvent / other
  • Не удалось получить сертификат SSL-клиента, работающий в Tornado
  • Получение заголовков браузера в Python
  • Почему мой WebSocket автоматически закрывается?
  • Python - лучший язык программирования в мире.