Учебное пособие по Python async/await
Last updated
Was this helpful?
Last updated
Was this helpful?
Асинхронное программирование набирает обороты в последние несколько лет, и не зря. Хотя это может быть сложнее, чем традиционный линейный стиль, он также намного эффективнее.
Например, вместо ожидания завершения HTTP-запроса перед продолжением выполнения с помощью асинхронных сопрограмм Python вы можете отправить запрос и выполнить другую работу, ожидающую в очереди, пока ожидает завершения HTTP-запроса. Чтобы понять правильную логику, может потребоваться немного больше размышлений, но вы сможете справиться с гораздо большей работой с меньшими ресурсами.
Даже тогда синтаксис и выполнение асинхронных функций в таких языках, как Python, на самом деле не так уж и сложны. Другое дело, с JavaScript, но Python, похоже, выполняет его довольно хорошо.
Асинхронность, кажется, главная причина того, почему Node.js так популярен для серверного программирования. Большая часть кода, который мы пишем, особенно в тяжелых приложениях ввода-вывода, таких как веб-сайты, зависит от внешних ресурсов. Это может быть что угодно, от удаленного вызова базы данных до POSTing в службу REST. Как только вы запрашиваете какой-либо из этих ресурсов, ваш код ждет, ничего не делая.
При асинхронном программировании вы позволяете своему коду обрабатывать другие задачи, ожидая ответа от других ресурсов.
Асинхронная функция в Python обычно называется сопрограммой, которая представляет собой просто функцию, использующую ключевое слово async или декоратор @asyncio.coroutine. Любая из приведенных ниже функций будет работать как сопрограмма и фактически эквивалентна по типу:
Это специальные функции, которые при вызове возвращают объекты сопрограмм. Если вы знакомы с обещаниями JavaScript, то можете думать об этом возвращаемом объекте почти как об обещании. Вызов любого из них на самом деле не запускает их, но вместо этого возвращается объект , который затем может быть передан в цикл событий для последующего выполнения.
На случай, если вам когда-нибудь понадобится определить, является ли функция сопрограммой или нет, asyncio предоставляет метод asyncio.iscoroutinefunction(func), который делает именно это за вас. Или, если вам нужно определить, является ли объект, возвращаемый функцией, объектом сопрограммы, вы можете вместо этого использовать asyncio.iscoroutine(obj).
Есть несколько способов вызвать сопрограмму, один из которых - метод yield from. Это было введено в Python 3.3 и было улучшено в Python 3.5 в форме async/await (о которой мы поговорим позже).
Выражение yield from может использоваться следующим образом:
Как видите, yield from используется в функции, украшенной @asyncio.coroutine. Если бы вы попытались использовать yield from вне этой функции, то получили бы такую ошибку от Python:
Чтобы использовать этот синтаксис, он должен находиться в другой функции (обычно с декоратором сопрограммы).
Более новый и понятный синтаксис заключается в использовании ключевых слов async/await. Представленный в Python 3.5, async используется для объявления функции как сопрограммы, подобно тому, как это делает декоратор @asyncio.coroutine. Его можно применить к функции, поместив его перед определением:
Чтобы вызвать эту функцию, мы используем await вместо yield from, но во многом таким же образом:
Опять же, как и yield from, вы не можете использовать это вне другой сопрограммы, иначе вы получите синтаксическую ошибку.
В Python 3.5 поддерживаются оба способа вызова сопрограмм, но способ async/await должен быть основным синтаксисом.
Цикл событий предоставляет вам несколько функций:
Регистрация, выполнение и отмена отложенных вызовов (асинхронные функции)
Создание клиентских и серверных транспортов для связи
Создание подпроцессов и транспортов для связи с другой программой
Делегирование вызовов функций пулу потоков
Хотя на самом деле существует довольно много конфигураций и типов циклов событий, которые вы можете использовать, большинству программ, которые вы пишете, просто нужно будет использовать что-то вроде этого для планирования функции:
Последние три строки - это то, что нас здесь интересует. Он начинается с получения цикла событий по умолчанию (asyncio.get_event_loop ()), планирования и выполнения задачи async, а затем закрытия цикла, когда цикл завершен.
Функция loop.run_until_complete () фактически блокирует, поэтому она не вернется, пока не будут выполнены все асинхронные методы. Поскольку мы выполняем это только в одном потоке, он не может двигаться вперед, пока цикл выполняется.
Теперь вы можете подумать, что это не очень полезно, поскольку мы все равно блокируем цикл событий (вместо только вызовов ввода-вывода), но представьте, что вы обертываете всю свою программу в асинхронной функции, которая затем позволит вам запускать много асинхронных запросы одновременно, как на веб-сервере.
Вы даже можете прервать цикл обработки событий в отдельный поток, позволяя ему обрабатывать все длинные запросы ввода-вывода, в то время как основной поток обрабатывает логику программы или пользовательский интерфейс.
Хорошо, давайте посмотрим на более крупный пример, который мы действительно можем запустить. Следующий код представляет собой довольно простую асинхронную программу, которая извлекает JSON из Reddit, анализирует JSON и распечатывает самые популярные сообщения дня из /r/python, /r/programming и /r/compsci.
Первый показанный метод, get_json (), вызывается get_reddit_top () и просто создает HTTP-запрос GET на соответствующий URL-адрес Reddit. Когда это вызывается с помощью await, цикл событий может продолжаться и обслуживать другие сопрограммы, ожидая ответа HTTP. Как только это произойдет, JSON возвращается в get_reddit_top (), анализируется и распечатывается.
Это немного отличается от примера кода, который мы показали ранее. Чтобы запустить несколько сопрограмм в цикле событий, мы используем asyncio.ensure_future (), а затем запускаем цикл навсегда, чтобы обработать все.
Чтобы запустить это, вам нужно сначала установить aiohttp, что вы можете сделать с помощью PIP:
Теперь просто убедитесь, что вы запускаете его с Python 3.5 или выше, и вы должны получить следующий результат:
Обратите внимание, что если вы запустите это несколько раз, порядок, в котором печатаются данные субреддита, изменится. Это связано с тем, что каждый из наших вызовов освобождает (передает) контроль над потоком, позволяя выполнить другой HTTP-вызов. То, что вернется первым, распечатывается первым.
Хотя встроенные в Python асинхронные функции не так удобны, как JavaScript, это не значит, что вы не можете использовать их для интересных и эффективных приложений. Просто потратьте 30 минут, чтобы изучить его все входы и выходы, и вы гораздо лучше поймете, как вы можете интегрировать это в свои собственные приложения.
Ни один из описанных выше сопрограмм не будет иметь значения (или работать), если вы не знаете, как стартовать и запускать . Цикл событий - это центральная точка выполнения асинхронных функций, поэтому, когда вы действительно хотите выполнить сопрограмму, это то, что вы будете использовать.