Перейти к содержимому

Async def python что это

  • автор:

Использование async/await в Python

Почитал про использование async / await в других языках программирования и не совсем понял, как и когда их используют. Для чего они вообще нужны? С помощью них можно улучшить уже существующий код? В каких случаях не стоит их использовать?

PEP 492 introduced support for native coroutines and async / await syntax to Python 3.5. A notable limitation of the Python 3.5 implementation is that it was not possible to use await and yield in the same function body. In Python 3.6 this restriction has been lifted, making it possible to define asynchronous generators:

async def ticker(delay, to): """Yield numbers from 0 to *to* every *delay* seconds.""" for i in range(to): yield i await asyncio.sleep(delay) 

PEP 530 adds support for using async for in list, set, dict comprehensions and generator expressions:

result = [i async for i in aiter() if i % 2] 

Additionally, await expressions are supported in all kinds of comprehensions:

result = [await fun() for fun in funcs if await condition()] 

Отслеживать
задан 24 дек 2016 в 22:20
Trajectory Trajectory
181 3 3 золотых знака 4 4 серебряных знака 11 11 бронзовых знаков

1 ответ 1

Сортировка: Сброс на вариант по умолчанию

Смотрите. Async/await нужен для того, чтобы не блокировать поток выполнения на время ожидания какого-нибудь асинхронного события. Конструкция Async/await превращает по сути процедуру в корутину (сопрограмму): она прекращает своё выполнение на время await , дожидается асинхронного события, и возобновляет работу.

В не-async-варианте ожидание получается блокирующим, или нужно вручную делать трюки: запускать операцию и подписываться на её окончание. Async делает код более простым, линейным.

Пример (на псевдокоде):

DownloadToFile(url): filename = GetFilename() content = await DownloadUrl(url) WriteToFile(filename, content) ReportSuccess() 
DownloadToFile(url): filename = GetFilename() BeginDownloadUrl(url, onfinished: lambda content: StoreContent(content, filename)) StoreContent(content, filename) WriteToFile(filename, content) ReportSuccess() 

Вы видите, что без async контекст выполнения (локальные переменные и т. п.) приходится передавать в «хвост» функции (continuation) вручную. Если async-вызовов много, аналогичный код без async быстро становится сложным.

Важное отличие async- от синхронной функции — async-функция возвращается к вызывающему коду в момент первого выполнения await (если тот ещё не завершён). Вызывающий код может дождаться полного окончания работы при помощи await, а может и продолжить работу самостоятельно.

Использование async/await имеет смысл там, где у вас есть ожидание, не связанное с нагрузкой на процессор. Например, ожидание прихода данных из интернета, или чтения файла с диска. В этом случае вы освобождаете физический поток выполнения для системы, но логическое выполнение продолжается (после возврата из await ).

Что такое асинхронное программирование в Python

The Python snake managing multiple tasks asynchronously.

Асинхронное программирование является подходом в разработке программного обеспечения, который позволяет одновременно выполнять несколько задач без блокирования основного потока выполнения. В Python это достигается с помощью асинхронной библиотеки asyncio и ключевых слов async и await . ��

Преимущества асинхронного программирования

Асинхронное программирование позволяет улучшить производительность приложения, особенно при работе с вводом-выводом (I/O), таким как чтение и запись файлов, обращение к базам данных и веб-сервисам.

Преимущества асинхронного программирования включают:

  • Более эффективное использование ресурсов
  • Улучшенная отзывчивость приложений
  • Упрощение кода для параллельного выполнения задач

Основы асинхронного программирования в Python

Для использования асинхронного программирования в Python, необходимо знакомство с ключевыми словами async и await :

  • async используется для объявления асинхронной функции. Это означает, что функция будет возвращать объект coroutine , который можно выполнить асинхронно.
  • await используется внутри асинхронной функции для ожидания результата другой асинхронной операции. Это позволяет основному потоку продолжить выполнение других задач, пока ожидается результат.

Пример асинхронного кода на Python:

import asyncio async def my_coroutine(): print("Starting coroutine. ") await asyncio.sleep(1) print("Coroutine finished!") async def main(): await my_coroutine() asyncio.run(main())

Python-разработчик: новая работа через 9 месяцев
Получится, даже если у вас нет опыта в IT

Асинхронное программирование с использованием библиотеки asyncio

В Python стандартная библиотека asyncio предоставляет инструменты для работы с асинхронным программированием, такие как цикл событий (event loop), корутины, футуры (futures) и таски (tasks).

Цикл событий

Цикл событий ( event loop ) является сердцем асинхронного программирования. Он отвечает за запуск, приостановку и возобновление асинхронных задач.

Корутины

Корутины ( coroutines ) представляют собой основные строительные блоки асинхронного кода. Они являются специальными функциями, которые могут приостанавливать свое выполнение, чтобы дать возможность выполнять другие задачи.

Футуры и таски

Футуры ( futures ) и таски ( tasks ) являются объектами, которые представляют результат асинхронных операций. Футуры используются для ожидания результата операции, а таски представляют собой обертку вокруг корутин для выполнения их в цикле событий.

Заключение

Асинхронное программирование в Python позволяет создавать мощные и эффективные приложения, которые могут обрабатывать множество задач одновременно без блокирования основного потока. С помощью ключевых слов async и await , а также библиотеки asyncio , вы сможете создавать асинхронные приложения на Python с отличной производительностью. ��

Поддержка асинхронности async/await в Python

Внимание! Любой асинхронный код написанный на языке Python не будет работать без поддержки его распараллеливания во время выполнения.

Asyncio используется в качестве основы для нескольких асинхронных сред Python, которые предоставляют высокопроизводительные сетевые и веб-серверы, библиотеки подключений к базам данных, распределенные очереди задач и т. д.

Есть определенный список правил, касающийся использования команд async / await .

  • Функция, которая начинается с async def является сопрограммой.
  • В теле сопрограммы можно использовать выражения await , return или yield .
  • Использование в сопрограмме ключевого слова yield создает асинхронный генератор, через который можно итерироваться с помощью async for .
  • Использование в сопрограмме выражения async with запускает асинхронные контекстные менеджеры.
  • Сопрограммы не могут использовать выражение yield from . Это вызовет исключение SyntaxError .
  • Ключевое слово await можно использовать только в теле сопрограммы. Вызов await в другом месте спровоцирует исключение SyntaxError .
  • Ключевое слово await требует наличия awaitable объекта. Такими объектами могут быть другая сопрограмма или объект у которого определен метод __await__() .

Ключевое слово await позволяет сопрограмме отдать контроль назад в главный цикл, который содержит порядок исполнения всех сопрограмм.

async def process(): result = await func() return result 

Если Python встречает ключевое слово await то это можно описать так: — «отложить исполнение кода сопрограммы process() до тех пор, пока я не получу результат выполнения func() . В это время я займусь чем-нибудь другим».

Сопрограммы/корутины async def в Python

Внутри тела функции сопрограммы идентификаторы await и async становятся зарезервированными ключевыми словами. Выражения await, async for и async with могут использоваться только в телах функций сопрограмм.

Асинхронный async for в Python

Асинхронный оператор `async for . in` обеспечивает удобную итерацию по асинхронным итераторам и асинхронным генераторам. Асинхронный оператор `async for . in` действует только в теле асинхронной функции (сопрограммы) `async def`.

Асинхронный контекст-менеджер async with в Python

Асинхронный менеджер контекста может приостановить выполнение в своих методах `__aenter__()` и `__aexit__()`. Асинхронный оператор `with` может использоваться только в теле асинхронной функции (сопрограммы).

Асинхронный итератор в Python

Асинхронный итератор может вызывать асинхронный код в своем методе `__anext__` и могут использоваться только в асинхронном операторе `async for`.

Асинхронный генератор в Python

Наличие выражения yield в функции или методе, определенном с использованием async def, дополнительно определяет функцию как функцию асинхронного генератора.

  • ОБЗОРНАЯ СТРАНИЦА РАЗДЕЛА
  • Сопрограммы/корутины async def
  • Асинхронный async for
  • Асинхронный контекст-менеджер async with
  • Асинхронный итератор
  • Асинхронный генератор

Корутины в Python

Когда говорят «написать корутину», обычно подразумевают асинхронную функцию. Корутины можно ставить на паузу, чтобы дать другим функциям немного поработать. В этом заключается принцип асинхронности. О нём мы рассказывали в этой статье.

Давайте сразу рассмотрим пример асинхронной функции:

import asyncio async def count_to_three(): print("Веду отсчёт. 1") await asyncio.sleep(0) print("Веду отсчёт. 2") await asyncio.sleep(0) print("Веду отсчёт. 3") await asyncio.sleep(0) 

Очень похоже на обычную функцию, однако здесь есть два новых слова: async и await .

async говорит Питону о том, что мы пишем не просто функцию, а асинхронную функцию. Просто добавили async и всё, функция теперь асинхронная.

Второе слово — await. Оно прерывает исполнение функции, и возвращает управление программой наружу. После этого корутину можно запустить повторно, а затем еще и еще, и каждый раз она будет продолжать работу с того await , на котором прервалась ранее. Например, в функции count_to_three команда await встречается три раза, значит корутину можно вызвать четыре раза (да, не три!). Корутина будет работать до первого await, затем до второго, до третьего и на четвёртый раз выполнит остатки до конца.

Нельзя делать await None или await «Hello, World!» . Можно await только то, что так и называют — «awaitable».

await asyncio.sleep(0) — это команда корутине «Дай поработать другим!»

Сразу покажем, как это выглядит на практике:

coroutine_counter = count_to_three() print(coroutine_counter) # coroutine_counter.send(None) # Выведет "Веду отсчёт. 1" coroutine_counter.send(None) # Выведет "Веду отсчёт. 2" coroutine_counter.send(None) # Выведет "Веду отсчёт. 3" 

Мы вызываем асинхронную функцию count_to_three , однако она не выводит на экран цифру 1, а возвращает корутину. Все асинхронные функции так делают. Это сделано для того, чтобы у вас был объект этой корутины в переменной. Теперь корутину можно запускать раз за разом, а она раз за разом будет делать кусочек и останавливаться на следующем await .

Чтобы запустить корутину, используют метод send() . При каждом запуске корутины этим методом она продолжает исполняться с последнего await , на котором она остановилась. Поэтому при новом запуске той же корутины срабатывает не тот же print , а следующий.

Нельзя просто .send() . Всегда нужно передавать какое-то значение. Об этом тоже расскажем позже. Пока что воспринимайте .send(None) как команду «продолжи выполнять корутину».

Когда корутина закончится?

Она остановится навсегда, когда закончатся все await или встретится return . Когда корутина заканчивается — она истощается и вызов .send() выдаёт ошибку:

coroutine_counter = count_to_three() coroutine_counter.send(None) # Выведет "Веду отсчёт. 1" coroutine_counter.send(None) # Выведет "Веду отсчёт. 2" coroutine_counter.send(None) # Выведет "Веду отсчёт. 3" coroutine_counter.send(None) # Выбросит ошибку StopIteration 

Если мы хотим запустить наш счётчик сначала, придётся создать новую корутину, вызвав count_to_three() :

coroutine_counter = count_to_three() coroutine_counter.send(None) # Выведет "Веду отсчёт. 1" coroutine_counter.send(None) # Выведет "Веду отсчёт. 2" coroutine_counter_new = count_to_three() coroutine_counter_new.send(None) # Снова выведет "Веду отсчёт. 1", новая корутина 

Обычно заранее не известно сколько await будет до момента «истощения», поэтому исключение приходится «перехватывать»:

coroutine_counter = count_to_three() while True: try: coroutine_counter.send(None) # В четвёртый раз здесь вылетит StopIteration except StopIteration: break 

Исключение StopIteration возникает всего один раз. Если после него попробовать запустить корутину ещё раз, то поднимется другое исключение — RuntimeError , и оно уже будет считаться ошибкой. О том как работать с исключениями читайте в статье про try except.

Нельзя запускать истощённую корутину.

Добиваемся асинхронности

С корутинами разобрались, останавливать их научились. А зачем.

Корутины позволят вашему коду работать асинхронно, т.е. делать несколько вещей одновременно. Допустим, вы решили скачать несколько файлов. Обычный, синхронный код скачивает файлы по-очереди. Сначала первый файл целиком, затем второй, тоже целиком. Асинхронный код качает файлы одновременно, по кусочкам. Приведём пример скачивания двух файлов:

async def download_file(url): # здесь происходит какая-то логика со скачиванием файла image_downloader = download_file('https://www.some-images.com/image1.jpg') music_downloader = download_file('https://www.music-site.com/artist/album/song5.mp3') coroutines = [music_downloader, image_downloader] while True: for coroutine in coroutines.copy(): try: coroutine.send(None) except StopIteration: coroutines.remove(coroutine) if len(coroutines) == 0: break 

Разберём как работает код:

  1. Мы создали 2 корутины: image_downloader и music_downloader . Первая качает картинку по ссылке https://www.some-images.com/image1.jpg , вторая — музыку по ссыке https://www.music-site.com/artist/album/song5.mp3 .
  2. Мы положили их в список coroutines
  3. В бесконечном цикле мы по очереди запускаем все корутины из списка. Если вышла ошибка StopIteration — корутина истощилась, т.е. файл скачан. Убираем её из списка, корутина больше запускаться не будет.
  4. Чтобы итерация по списку coroutines не сбивалась после удаления элемента из него итерируем не по оригиналу, а по копии coroutines.copy() .
  5. Если список с корутинами закончился (его длина равна нулю), пора заканчивать и бесконечный цикл, потому что все файлы скачаны.

Передать параметры в асинхронную функцию

В плане аргументов асинхронные функции ничем не отличаются от обычных. Доработаем пример со счетчиком и вместо async def count_to_three напишем универсальную функцию async def count :

import asyncio async def count(limit=3): for step in range(1, limit+1): print("Веду отсчёт.", step) await asyncio.sleep(0) coroutine = count(5) while True: coroutine.send(None) 
Веду отсчёт. 1 Веду отсчёт. 2 Веду отсчёт. 3 Веду отсчёт. 4 Веду отсчёт. 5 Traceback (most recent call last): File "", line 2, in StopIteration 

Попробуйте бесплатные уроки по Python

Получите крутое код-ревью от практикующих программистов с разбором ошибок и рекомендациями, на что обратить внимание — бесплатно.

Переходите на страницу учебных модулей «Девмана» и выбирайте тему.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *