2015-03-29 2 views
15

У меня изначально был код, который агрегировал результаты в список. Когда я переработан этот код, чтобы использовать список comphrehension, я получаю неожиданные результаты:Почему я получаю разные результаты при использовании понимания списка с сопрограммами с asyncio?

import asyncio 

@asyncio.coroutine 
def coro(): 
    return "foo" 


# Writing the code without a list comp works, 
# even with an asyncio.sleep(0.1). 
@asyncio.coroutine 
def good(): 
    yield from asyncio.sleep(0.1) 
    result = [] 
    for i in range(3): 
     current = yield from coro() 
     result.append(current) 
    return result 


# Using a list comp without an async.sleep(0.1) 
# works. 
@asyncio.coroutine 
def still_good(): 
    return [(yield from coro()) for i in range(3)] 


# Using a list comp along with an asyncio.sleep(0.1) 
# does _not_ work. 
@asyncio.coroutine 
def huh(): 
    yield from asyncio.sleep(0.1) 
    return [(yield from coro()) for i in range(3)] 


loop = asyncio.get_event_loop() 
print(loop.run_until_complete(good())) 
print(loop.run_until_complete(still_good())) 
print(loop.run_until_complete(huh())) 

Если я запускаю этот код я получаю этот выход:

$ python3.4 /tmp/test.py 
['foo', 'foo', 'foo'] 
['foo', 'foo', 'foo'] 
<generator object <listcomp> at 0x104eb1360> 

Почему я получаю разные результаты для третьего huh() функционировать?

+0

[Whoa, это воспроизводимый.] (Http://ideone.com/k2MsG9) Я этого не ожидал. Как, черт возьми? – user2357112

ответ

7

Исправление вашей проблемы будет поставить next(...) вместо ... в возвращении третьей функции, или лучше напишите return list((yield from coro()) for i in range(3)) (кредитов @zch этой идеи), или даже лучше остаться с первой функцией.


Дело в том, что вторая функция не является генератором. Это просто обычная функция, которая возвращает генератор понимания.

Например, этот код является действительным вне генератора:

values = [(yield x) for x in range(3)] 

Тогда вы можете сделать это:

next(values) 
0 
next(values) 
1 
next(values) 
2 
next(values) 
Traceback (most recent call last): 
File "<stdin>", line 1, in <module> 
StopIteration: [None, None, None] 

декоратор @coroutine затем делает вторую функцию генератора итерации на результате, см. here, строка 143.

Напротив, первый и третий rd-функции на самом деле являются генераторами, а декораторы @coroutine просто возвращаются, см. here, строки 136-137. В первом случае генератор возвращает список (актуальный рейз StopIteration(['foo', 'foo', 'foo'])). В третьем случае он возвращает генератор понимания.

+1

Подождите, что делает выражение yield внутри понимания списка? – user2357112

+0

@ user2357112 он делает его генератором, а не списком. См. 'Type ([(yield x) для x в диапазоне (3)])' даст вам ''. В то же время 'type ([x for x в диапазоне (3)])' is ''. – ivanl

+0

После некоторого копания, я могу понять, почему он работает так, как он есть, но это действительно должно быть SyntaxError, а не то, что на самом деле происходит. Люди, которые знают о поведении, не будут использовать его, потому что это так запутывает, и люди, которые не знают об этом, получат бесшумный код. – user2357112

 Смежные вопросы

  • Нет связанных вопросов^_^