Подтвердить что ты не робот

Проектирование асинхронного API в Python

(Примечание: этот вопрос строго касается дизайна API, а не того, как его реализовать, то есть я забочусь только о том, что видит клиент моего API, а не о том, что я должен сделать, чтобы он работал.)

В простых терминах: я хочу знать установленный шаблон - если есть - для явных фьючерсов (aka promises, aka отложенные, а также имена задач варьируются в зависимости от на основе) в Python. Ниже приведено более подробное описание.

Рассмотрим простой API-интерфейс Python, например:

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 и т.д.) И подключиться к функции С# 5.0 async.

В другом примере, в JavaScript, в стандартной библиотеке нет ничего, чтобы покрыть это, но jQuery популяризировал интерфейс "отложенного обещания", который теперь отдельно указан. Поэтому, если я должен написать async readLine в JS, я бы назвал его readLineAsync и внедрил метод then в возвращаемое значение.

Что, если таковые имеются, является установленным образцом на земле Питона? Просматривая стандартную библиотеку, я вижу несколько модулей, предлагающих асинхронные API, но между ними нет согласованного шаблона и ничего подобного стандартизованному протоколу для "задач" или "promises". Возможно, существует некоторая модель, которая может быть получена из популярных сторонних библиотек?

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

4b9b3361

Ответ 1

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

Это звучит как разумный подход? Если это, возможно, будет сопровождаться предложением добавить абстрактный базовый класс для общей "будущей" концепции в стандартную библиотеку Python, так же как коллекции Python имеют свои ABCs.

Ответ 2

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

Хорошим примером является BaseHTTPServer

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

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

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

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

read_line_async( AsyncReadHandler )

Что-то вроде этого будет следовать некоторым установленным шаблонам проектирования.

Ответ 3

Вы еще не посмотрели декораторы

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)