我正在努力写一些尽可能惯用的东西来收集存储在字典中的期货的结果。
我们假设我有以下代码:
import asyncio
async def sleep(seconds):
print(f'sleeping for {seconds} seconds')
await asyncio.sleep(seconds)
print(f'finished sleeping {seconds} seconds')
async def run():
tasks = {
'4': sleep(4),
'3': sleep(3),
'2': sleep(2),
'1': sleep(1),
}
print(await gather_from_dict(tasks))
if __name__ == '__main__':
asyncio.get_event_loop().run_until_complete(run())
我期待的输出是:
sleeping for 2 seconds
sleeping for 1 seconds
sleeping for 4 seconds
sleeping for 3 seconds
finished sleeping 1 seconds
finished sleeping 2 seconds
finished sleeping 3 seconds
finished sleeping 4 seconds
{'4': None, '3': None, '2': None, '1': None}
到目前为止,我找到的最干净的解决方案是:
async def gather_from_dict(tasks:Dict[Hashable, Awaitable],
loop=None, return_exceptions=False) -> Dict:
results = await asyncio.gather(
*tasks.values(),
loop=loop,
return_exceptions=return_exceptions
)
return dict(zip(tasks.keys(), results))
有关如何以更简单的方式执行此操作的任何想法? 感谢!!!
答案 0 :(得分:0)
我重新定义了你的任务,使它更纯粹作为协程列表,并且更喜欢从run_until_complete方法得到结果,代码如下,并注意我在你的睡眠代码中返回一些内容,在你的代码中,你实际上返回None
import asyncio
async def sleep(seconds):
print('sleeping for {seconds} seconds'.format(seconds=seconds))
await asyncio.sleep(seconds)
print('finished sleeping {seconds} seconds'.format(seconds=seconds))
return {seconds: 'value of {seconds}'.format(seconds=seconds)}
if __name__ == '__main__':
loop = asyncio.get_event_loop()
tasks = [sleep(i) for i in range(1, 5)]
finished, _ = loop.run_until_complete(
asyncio.wait(tasks))
result = {}
for task in finished:
result.update(task.result())
print(result)
loop.close()
答案 1 :(得分:0)
假设: 期货(或协程)将位于列表和字典的结构中,而不位于任何自定义对象或元组中!
给出如下一些异步函数:
import asyncio
async def slow(result):
await asyncio.sleep(0.1)
return result
async def some_complex_stuff_here():
return {
'a': slow(1),
'b': [
slow('hello'),
{
'c': slow('fna'),
1: slow('world'),
}
],
}
您可以使用以下代码等待所有内容:
def __gather_futures(container, path, futures):
if isinstance(container, dict):
for key, value in container.items():
sub_path = path + (key, )
__gather_futures(value, sub_path, futures)
if isinstance(container, list):
for idx, value in enumerate(container):
sub_path = path + (idx,)
__gather_futures(value, sub_path, futures)
if inspect.isawaitable(container):
futures.append((path, container))
async def gather_object_futures(container):
futures = []
wrapper = {'content': container}
__gather_futures(container=wrapper, path=(), futures=futures)
results = await asyncio.gather(*[f[1] for f in futures])
for (path, future), result in zip(futures, results):
current_object = wrapper
for key in path[:-1]:
current_object = current_object[key]
current_object[path[-1]] = result
return wrapper['content']
要调用此方法,您可以使用:
async def run():
return await gather_object_futures(await some_complex_stuff_here())
import time
time_start = time.time()
loop = asyncio.get_event_loop()
result = loop.run_until_complete(run())
# will print something like 100ms --> all futures have been gathred at once!
print(time.time() - time_start)
# result contains all of the resolved results
print(result
请注意:在collect_object_futures函数调用中的等待至关重要!