为什么在使用带有asyncio的协同程序的列表解析时会得到不同的结果?

时间:2015-03-29 19:53:26

标签: python python-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()函数获得不同的结果?

1 个答案:

答案 0 :(得分: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行。

相反,第一个和第三个函数实际上是生成器,@coroutine装饰器只返回自己,参见here,第136-137行。在第一种情况下,生成器返回列表(实际上引发StopIteration(['foo', 'foo', 'foo']))。在第三种情况下,它返回理解生成器。