首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在用Python进行coroutine尾调用时,使用或避免coroutines是更多的Pythonic (和/或表演性)吗?

在用Python进行coroutine尾调用时,使用或避免coroutines是更多的Pythonic (和/或表演性)吗?
EN

Stack Overflow用户
提问于 2017-05-28 01:46:06
回答 2查看 418关注 0票数 6

在Python中,我通常会遇到这样一种情况:我有许多嵌套的3.5+,只是为了调用一个深度为协同的东西,在大多数函数中,await只是在尾部调用,如下所示:

代码语言:javascript
复制
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()

这些函数abc可以编写为返回协同线的正则函数,而不是作为协同线本身编写,如下所示:

代码语言:javascript
复制
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()

哪条路更像毕多诺?哪条路更有表现力?对其他人来说,未来哪一种方式更容易维护?

编辑-性能测量

作为性能测试,我从await asyncio.sleep(time)中删除了deep行,并对await a(0.1)进行了1,000,000次迭代。在我使用CPython 3.5.2的测试系统上,第一个版本大约需要2.4秒,第二个版本大约需要1.6秒。所以看起来做任何事情都会有性能上的损失,但这肯定不是一个数量级。也许拥有更多经验的Python代码可以创建一个适当的基准,并最终解决性能问题。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-05-28 09:15:09

使用第一个:您不仅可以显式地显示代码可以挂起的位置(放置await的位置),还可以获得所有相关的好处,比如显示有帮助的执行流的跟踪。

要查看两者之间的差异,请更改您的deep协同值以抛出一些错误:

代码语言:javascript
复制
async def deep(time):
    await asyncio.sleep(time)
    raise ValueError('some error happened')
    return time

对于第一个片段,您将看到以下输出:

代码语言:javascript
复制
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

但只适用于第二个片段:

代码语言:javascript
复制
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

正如您所看到的,第一次回溯帮助您看到“真实”(和有帮助的)执行流程,而第二次跟踪帮助您查看“真实”(且有帮助的)执行流程。

第一种编写代码的方法也要好得多:想象一下,一旦理解了b(time)也应该包含一些异步调用,比如await asyncio.sleep(time)。在第一个片段中,这个调用可以直接放置,而不需要进行任何其他更改,但是在第二个片段中,您必须重写代码的许多部分。

票数 5
EN

Stack Overflow用户

发布于 2017-05-30 05:43:18

这是一个罕见的例子,“这是毕达通吗?”其实不是一个基于意见的问题。尾呼叫优化正式是非Pythonic的:

所以让我来为我的立场辩护(我不想在语言中消除尾递归)。如果你想要一个简短的答案,那就是非丙酮- BDFL

(另请参阅)

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/44223090

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档