【发布时间】:2017-06-11 18:45:42
【问题描述】:
短篇小说:
yield 不能在async def 函数中用于暂停执行并返回给调用者。在 Python 3.5 中是否有其他方法可以做到这一点?
编辑: a very similar question 的答案建议使用 Python 3.6 或将异步生成器定义为一个类。不幸的是,前者目前不是一个选择。生成器可以工作,但为此目的定义一个类似乎比我自己的尝试更笨重。
大概没有干净简单的解决方案吧?
长篇大论:
我需要编写函数来暂停它们的操作,返回到调用者,然后在它们停止的地方继续。这些可以实现为状态机,可能使用类,这会撕裂相关代码并且通常相当不可读。借助生成器,Python 提供了一种或多或少优雅的方式来编写此类函数:
def a():
print('A Part 1')
yield # suspend execution to caller
print('A Part 2')
def run(func):
o = func()
try:
while True:
print('running...')
o.send(None)
except StopIteration:
pass
run(a)
# running...
# A Part 1
# running...
# A Part 2
Python 3.3 添加了yield from 语法,它可以很好地嵌套这些函数。 yield from 不是将执行返回给调用者,而是将执行转移到另一个函数:
def b():
print('B Part 1')
yield from a()
print('B Part 2')
run(b)
# running...
# B Part 1
# A Part 1
# running...
# A Part 2
# B Part 2
Python 3.5 引入了async def 和await 来区分协程和生成器。当然我宁愿使用这些原生协程。重写b 很容易,只需将def 替换为async def 并将yield from 替换为await。但是,我没有找到一种规范的方式来暂停协程并返回给调用者。 yield 在异步函数中是不允许的,await 只需要另一个函数来运行。我想出了这个尴尬的解决方案:
import asyncio
@asyncio.coroutine
def awkward_suspend():
yield
async def c():
print('C Part 1')
#yield # SyntaxError: 'yield' inside async function
#await # Syntax Error: invalid syntax
await awkward_suspend()
print('C Part 2')
run(c)
# running...
# C Part 1
# running...
# C Part 2
这种方法将yield 包装在一个普通函数中,从而创建一个生成器,并将生成器标记为协程,以便它可以被awaited。
这感觉就像是对语言的滥用。没有asyncio 和笨拙的挂起功能,有没有办法达到同样的效果?
【问题讨论】:
-
谢谢@JoshLee,我想知道我是如何在搜索中错过了这个问题的。尽管它似乎与我的问题有一些共同点,但我相信它们是相当不同的。该问题的 OP 正在寻找一种从异步函数中产生(返回)值的方法。相比之下,我需要暂停一个异步函数。
标签: python python-3.5