在Python中设计异步API

12 投票
3 回答
5438 浏览
提问于 2025-04-17 01:11

(注意:这个问题主要是关于API的设计,而不是如何实现它;也就是说,我只关心我的API的使用者看到的内容,而不是我需要做什么才能让它工作。)

简单来说:我想了解在Python中关于显式未来(也叫承诺、延迟、任务 - 名字根据框架不同而不同)的已有模式。如果有的话,以下是更详细的描述。

考虑一个简单的Python API,如下所示:

def read_line():
   ...
s = read_line()
print(s)

这是一个同步版本 - 如果一行数据还没有准备好,它会阻塞等待。现在,假设我想提供一个对应的异步(非阻塞)版本,允许注册一个回调函数,在操作完成后调用它。例如,一个简单的版本可能看起来像这样:

def read_line_async(callback):
   ...
read_line_async(lambda s: print(s))

在其他语言和框架中,通常会有一些现成的或至少是公认的模式来处理这样的API。例如,在.NET 4之前,通常会提供一对BeginReadLine/EndReadLine方法,并使用标准的IAsyncResult接口来注册回调和传递结果值。在.NET 4及以上版本中,使用System.Threading.Tasks,以便启用所有任务组合操作符(WhenAll等),并与C# 5.0的async特性结合使用。

再举个例子,在JavaScript中,标准库没有覆盖这个内容,但jQuery普及了“延迟承诺”接口,现在已经单独规范化。所以如果我在JS中编写异步的readLine,我会把它命名为readLineAsync,并在返回值上实现then方法。

那么,在Python中,有没有什么公认的模式呢?浏览标准库时,我看到几个模块提供异步API,但它们之间没有一致的模式,也没有类似于“任务”或“承诺”的标准协议。也许可以从一些流行的第三方库中推导出某种模式?

我还看到在这个上下文中经常提到的Deferred类在Twisted中,但它似乎为通用的承诺API设计得过于复杂,更适应于这个库的特定需求。看起来我很难轻松地克隆一个接口(而不依赖于它们),以便我们的承诺能够很好地与客户端在应用中同时使用这两个库进行交互。有没有其他流行的库或框架,专门为此设计了API,我可以在不直接依赖的情况下进行复制(并实现互操作)?

3 个回答

0

你有没有看过装饰器呢?

from threading import Thread

def addCallback(function):
    def result(parameters,callback):
        # Run the function.
        result = function(parameters)
        # Run the callback asynchronously.
        Thread(target=callback).start()
        # Run the callback synchronously.
        #callback()
        # Return the value of the function.
        return result
    return result

@ addCallback
def echo(value):
    print value

def callback():
    print 'Callback'

echo('Hello World!',callback)
1

可以看看各种“服务器”库,里面有很多提示。

一个很好的例子是 BaseHTTPServer

特别是 HTTPServer 这个类的定义,展示了如何提供一个“处理器类”。

每次请求都会创建一个处理器类的实例。这个对象就会处理这个请求。

如果你想写“异步输入输出”,并使用“回调”,你需要给你的读取器提供一个 ReadHandler 类。

class AsyncReadHandler( object ):
    def input( self, line, server ):
        print( line )

read_line_async( AsyncReadHandler )

这样的做法会遵循一些已经建立的设计模式。

6

好的,我找到了 PEP-3148,里面确实有一个 Future 类。不过,我发现我不能直接使用它,因为只有通过 Executor 类才能创建正确的实例。这个 Executor 类的作用是把现有的同步接口转换成异步的,比如把同步调用放到后台线程去执行。不过,我可以完全复制 Future 对象提供的方法,它们和我预期的非常接近,比如可以查询结果(可能会阻塞)、取消操作和添加回调函数。

这样做听起来合理吗?是不是应该提出一个建议,给 Python 标准库添加一个通用的“future”概念的抽象基类,就像 Python 的集合有它们的 抽象基类 一样。

撰写回答