1+1=10

扬长避短 vs 取长补短

Python 协程(Coroutine)小记(二)

接前面 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 defawait明明设计上都不依赖 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__()成员。

generator-asyncgenerator-coroutine-classes

扩充一下?

加点其他试试?

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

...

参考

Comments