异步可调用

在正式开始我们的教程之前,需要简单讲一讲关于“异步可调用”的概念。

首先关于“可调用”或“可调用对象”,大家在过去的 Python 开发中,应该有所接触。可调用对象代指任何能被调用的东西,像是一种接口,都能使用 () 来触发调用行为:

class Foo:
    def __call__(self, *args, **kwargs):
        ...
foo = Foo()

bar1 = lambda *_, **__: ...

def bar2(*args, **kwargs):
    ...

from functools import partial
bar3 = partial(bar2, 1, arg1=2)

# 它们都是可调用对象,因此可以:
foo()
bar1()
bar2()
bar3()

可调用对象一般使用 Callable 注解:

f: Callable = ...

同样的思路,melobot 中提出了 AsyncCallable 类型。它用于许多接口的类型注解,有以下特性:

AsyncCallable[P, T] \(\iff\) Callable[P, Awaitable[T]]

其中 P 为 ParamSpec 类型变量,T 为普通的无约束类型变量。

典型的异步可调用对象包括:

class Foo:
    async def __await__(self):
        ...

async def _any_coro_f(*args, **kwargs): ...
bar1 = lambda *_, **__: _any_coro_f(*_, **__)

async def bar2(*args, **kwargs):
    ...

from functools import partial
bar3 = partial(bar2, 1, arg1=2)

# 它们都是异步可调用对象,因此可以:
await Foo()
await bar1()
await bar2()
await bar3()

在 melobot 中,也提供了装饰器 to_async() 转换可调用为异步可调用:

# 在一般函数上装饰,转换为异步可调用
@to_async
def sync_func(...):
    ...

# 直接调用,适用于各种可调用对象:
async def _any_coro_f(*args, **kwargs): ...
f = to_async(lambda: _any_coro_f(1, 2, 3))

aprint = to_async(print)

to_async() 只是将原对象包裹在一个异步函数中,从而满足异步可调用的接口。

即:to_async() 不做接口兼容外的处理,因此也就不会提供并发/并行的能力。