在Python 3.5+中,我常常遇到这样一种情况,即我有许多嵌套协程,只是为了调用一些深层次的协程,await
只是在尾调用大多数功能,如:
import asyncio
async def deep(time):
await asyncio.sleep(time)
return time
async def c(time):
time *= 2
return await deep(time)
async def b(time):
time *= 2
return await c(time)
async def a(time):
time *= 2
return await b(time)
async def test():
print(await a(0.1))
loop = asyncio.get_event_loop()
loop.run_until_complete(test())
loop.close()
这些函数a
,b
和c
可以编写为返回协程的常规函数,而不是协程本身,如下所示:
import asyncio
async def deep(time):
await asyncio.sleep(time)
return time
def c(time):
time *= 2
return deep(time)
def b(time):
time *= 2
return c(time)
def a(time):
time *= 2
return b(time)
async def test():
print(await a(0.1))
loop = asyncio.get_event_loop()
loop.run_until_complete(test())
loop.close()
哪种方式更像Pythonic?哪种方式更高效?其他人在将来哪种方式更容易维护?
作为效果测试,我从await asyncio.sleep(time)
删除了deep
行,并计算了await a(0.1)
的1,000,000次迭代。在我的CPython 3.5.2测试系统上,第一个版本大约需要2.4秒,第二个版本需要大约1.6秒。因此看起来制作所有协同程序可能会有性能损失,但它肯定不是一个数量级。也许拥有更多Python代码分析经验的人可以创建一个合适的基准并明确解决性能问题。
答案 0 :(得分:5)
使用第一个:您不仅可以显式地显示代码可以挂起的位置(放置await
的位置),还可以获得所有相关的好处,例如显示有用执行流程的回溯。
要查看差异,请更改deep
协程以抛出错误:
async def deep(time):
await asyncio.sleep(time)
raise ValueError('some error happened')
return time
对于第一个片段,您会看到此输出:
Traceback (most recent call last):
File ".\tmp.py", line 116, in <module>
loop.run_until_complete(test())
File ".\Python36\lib\asyncio\base_events.py", line 466, in run_until_complete
return future.result()
File ".\tmp.py", line 113, in test
print(await a(0.1))
File ".\tmp.py", line 110, in a
return await b(time)
File ".\tmp.py", line 106, in b
return await c(time)
File ".\tmp.py", line 102, in c
return await deep(time)
File ".\tmp.py", line 97, in deep
raise ValueError('some error happened')
ValueError: some error happened
但仅限第二个片段:
Traceback (most recent call last):
File ".\tmp.py", line 149, in <module>
loop.run_until_complete(test())
File ".\Python36\lib\asyncio\base_events.py", line 466, in run_until_complete
return future.result()
File ".\tmp.py", line 146, in test
print(await a(0.1))
File ".\tmp.py", line 130, in deep
raise ValueError('some error happened')
ValueError: some error happened
正如您所看到的,首先追溯可以帮助您查看&#34;真实&#34; (并且有用)执行流程,而第二个不是。
编写代码的第一种方法也更好维护:假设您曾经理解b(time)
还应该包含一些异步调用,例如await asyncio.sleep(time)
。在第一个代码段中,此调用可以直接放置而无需任何其他更改,但在第二个代码中,您必须重写代码的许多部分。
答案 1 :(得分:1)