接前面 Python 协程小记(一),继续整理Python 协程基础知识。随意记录下自己的纠结点
协程函数写好了:
async def hello(name):
print(f"Hello {name}!")
然后抓瞎了,要执行这个协程,只会用asyncio
:
import asyncio
asyncio.run(hello("1+1=10"))
抛开asyncio
, 竟不知道怎么执行它。乱试一通,通通都不行:
hello("1+1=10") # RuntimeWarning: coroutine 'hello' was never awaited
hello("1+1=10").send(None) # TypeError: object NoneType can't be used in 'await' expression
await hello("1+1=10") # SyntaxError: 'await' outside function
hello("1+1=10").__await__().send(None) # TypeError: object NoneType can't be used in 'await' expression
next(hello("1+1=10").__await__()) # TypeError: object NoneType can't be used in 'await' expression
这个问题让人堵得慌: async def
、await
明明设计上都不依赖 asyncio这个库的。为什么想运行一个小函数这么难...
开始-结束
准备好好挖一下,突然发现上面 hello("1+1=10").send(None)
已经接近答案了,只是看错误提示信息时,漏掉了 StopIteration
这句:
Hello 1+1=10!
Traceback (most recent call last):
File "C:\Users\dbzha\PycharmProjects\pythonProject2\b.py", line 11, in <module>
hello("1+1=10").send(None) # TypeError: object NoneType can't be used in 'await' expression
^^^^^^^^^^^^^^^^^^^^^^^^^^
StopIteration
修改一下,直接可以了:
async def hello(name):
print(f"Hello {name}!")
try:
f = hello("1+1=10")
f.send(None)
except StopIteration:
pass
和生成器(Generate Iterator)一样,我们可以使用send()函数进行驱动。但是和生成器不一样,我们不能用next()驱动它,因为协程没有__next__()
成员。
扩充一下?
加点其他试试?
Awaitable
定义一个 MyAwaitable,可以就可以用await了:
class MyAwaitable:
def __await__(self):
yield
async def hello():
print(f"Hello begin")
await MyAwaitable()
print(f"Hello end")
驱动起来,注意需要2个send():
try:
f = hello()
f.send(None)
f.send(None)
except StopIteration:
pass
从这儿可以看到,需要有个自动机制,来不断触发send()才行
异步迭代器
定义一个异步迭代器看看:
async def debao():
yield 'd'
yield 'e'
yield 'b'
yield 'a'
yield 'o'
async def hello():
print(f"Hello begin")
d = debao()
async for i in d:
print(i)
print(f"Hello end")
同样方式驱动:
try:
f = hello()
f.send(None)
except StopIteration:
pass
结果如下:
Hello begin
d
e
b
a
o
Hello end
思考,如果hello()中不用 async for
,会怎么样?:
async def hello():
print(f"Hello begin")
d = aiter(debao())
print(await anext(d))
print(await anext(d))
print(await anext(d))
print(await anext(d))
print(await anext(d))
print(f"Hello end")
try:
f = hello()
f.send(None)
f.send(None)
f.send(None)
f.send(None)
f.send(None)
except StopIteration:
pass
尽管丑陋了很多,结果和之前一样:
Hello begin
d
e
b
a
o
Hello end
还可以再调整一下:
async def debao():
yield 'd'
yield 'e'
yield 'b'
yield 'a'
yield 'o'
async def hello():
print(f"Hello begin")
d = aiter(debao())
while True:
try:
print(await anext(d))
except StopAsyncIteration:
break
print(f"Hello end")
if __name__ == '__main__':
try:
f = hello()
while True:
f.send(None)
except StopIteration:
pass
...
参考
- https://stackoverflow.com/questions/34469060/python-native-coroutines-and-send
- http://www.dabeaz.com/coroutines/
- https://docs.python.org/3.11/glossary.html#term-coroutine
- PEP 492 – Coroutines with async and await syntax