Разработка асинхронного API в Python - PullRequest
12 голосов
/ 02 сентября 2011

(Примечание: этот вопрос касается исключительно дизайна 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 популяризировал интерфейс «отложенного обещания», который теперь отдельно указан . Поэтому, если бы я написал async readLine в JS, я бы назвал его readLineAsync и реализовал метод then для возвращаемого значения.

Каков, если таковой имеется, установленный шаблон на земле Python? Просматривая стандартную библиотеку, я вижу несколько модулей, предлагающих асинхронные API, но между ними нет единого шаблона и ничего похожего на стандартизированный протокол для «задач» или «обещаний». Возможно, есть какой-то шаблон, который можно извлечь из популярных сторонних библиотек?

Я также видел (часто упоминаемый в этом контексте) Отложенный класс в Twisted, но, похоже, он переоценен для API обещаний общего назначения и скорее адаптирован к конкретным потребностям эта библиотека. Это не похоже на то, для чего я мог бы легко клонировать интерфейс (без зависимости от них), чтобы наши обещания хорошо взаимодействовали, если бы клиент использовал обе библиотеки вместе в своем приложении. Есть ли какая-либо другая популярная библиотека или фреймворк с явно разработанным API для этого, которую я мог бы копировать (и взаимодействовать) без прямой зависимости?

Ответы [ 3 ]

6 голосов
/ 02 сентября 2011

Хорошо, я нашел PEP-3148 , который имеет класс Future. Насколько я понимаю, я не могу использовать его как есть, потому что надлежащие экземпляры создаются только Executor, и это класс для преобразования существующих синхронных API в асинхронность, например, с помощью перемещение синхронного вызова в фоновый поток. Тем не менее, я могу точно воспроизвести методы, предоставляемые Future объектами - они очень близко соответствуют тому, что я ожидал, т. Е. Возможность (блокирования) запроса результата, отмены и добавления обратного вызова.

Это звучит как разумный подход? Должно ли оно сопровождаться предложением добавить абстрактный базовый класс для общей концепции «будущего» в стандартную библиотеку Python, точно так же, как коллекции Python имеют свои ABC .

1 голос
/ 02 сентября 2011

Прочитайте различные «серверные» библиотеки для подсказок.

Хороший пример: BaseHTTPServer

В частности, определение класса HTTPServer показывает, как предоставляется «класс обработчика».

Каждый запрос создает экземпляр класса обработчика. Затем этот объект обрабатывает запрос.

Если вы хотите написать «асинхронный ввод-вывод» с «обратным вызовом», вы должны предоставить вашему читателю класс ReadHandler.

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

read_line_async( AsyncReadHandler )

Нечто подобное будет следовать определенным шаблонам проектирования.

0 голосов
/ 02 сентября 2011

Вы уже смотрели на декораторов еще?

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)
...