Сопрограммы в Python: использование async и await
Резюме: в этом руководстве вы узнаете о сопрограммах (coroutines) в Python и о том, как использовать ключевые слова async и await для создания и приостановки сопрограмм.
- Что такое сопрограммы в Python?
- Определение сопрограммы с ключевым словом async в Python
- Приостановка сопрограммы с ключевым словом await в Python
Что такое сопрограммы в Python?
Сопрограмма (корутина) — это обычная функция в Python с возможностью приостановить ее выполнение при обнаружении операции, выполнение которой может занять некоторое время.
Когда длительная операция завершится, вы можете возобновить приостановленную сопрограмму и выполнить оставшийся код в этой сопрограмме.
Пока сопрограмма ожидает длительной операции, вы можете запустить другой код. Сделав это, вы сможете запускать программу асинхронно, чтобы повысить ее производительность.
Чтобы создать и приостановить корутину, используйте ключевые слова async и await:
- Ключевое слово async создает сопрограмму.
- Ключевое слово await приостанавливает выполнение сопрограммы.
Определение сопрограммы с ключевым словом async в Python
Ниже определяется простая функция, которая возвращает квадрат целого числа:
def square(number: int) -> int: return number*number
И вы можете передать целое число в функцию Square(), чтобы получить его квадрат:
def square(number: int) -> int: return number*number result = square(10) print(result)
Выход:
100
Когда вы добавляете ключевое слово async в функцию, функция становится сопрограммой:
async def square(number: int) -> int: return number*number
Вызывающая сопрограмма возвращает объект coroutine, который будет запущен позже. Например:
async def square(number: int) -> int: return number*number result = square(10) print(result)
Выход:
<coroutine object square at 0x00000185C31E7D80> sys:1: RuntimeWarning: coroutine 'square' was never awaited
В этом примере мы вызываем сопрограмму Square(), присваиваем возвращаемое значение переменной результата и распечатываем его.
Когда вы вызываете сопрограмму, Python не выполняет код внутри сопрограммы немедленно. Вместо этого он возвращает объект coroutine.
Вторая строка вывода также показывает сообщение об ошибке, указывающее, что сопрограмма никогда не ожидалась. Подробнее об этом в следующем разделе ожидания:
sys:1: RuntimeWarning: coroutine 'square' was never awaited
Чтобы запустить сопрограмму, вам необходимо выполнить ее в цикле событий. До Python 3.7 вам приходилось вручную создавать цикл событий для выполнения сопрограмм и закрытия цикла событий.
Однако, начиная с версии 3.7, в библиотеку asyncio добавлены некоторые функции, упрощающие управление циклом событий. Например, вы можете использовать функцию asyncio.run() для автоматического создания цикла событий, запуска сопрограммы и ее закрытия.
В следующем примере используется функция asyncio.run() для выполнения сопрограммы Square() и получения результата:
import asyncio async def square(number: int) -> int: return number*number result = asyncio.run(square(10)) print(result)
Выход:
100
Важно отметить, что asyncio.run() спроектирован как основная точка входа в программу asyncio. Кроме того, функция asyncio.run() выполняет только одну сопрограмму, которая может вызывать другие сопрограммы и функции в программе.
Приостановка сопрограммы с ключевым словом await в Python
Ключевое слово await приостанавливает выполнение сопрограммы. За ключевым словом await следует вызов сопрограммы, например:
result = await my_coroutine()
Ключевое слово await вызывает выполнение my_coroutine(), ожидает завершения кода и возвращает результат.
Важно отметить, что ключевое слово await действует только внутри сопрограммы. Другими словами, вы должны использовать ключевое слово await внутри сопрограммы. Именно по этой причине вы увидели сообщение об ошибке в приведенном выше примере, в котором ключевое слово await используется вне сопрограммы.
В следующем примере показано, как использовать ключевое слово await для приостановки сопрограммы:
import asyncio async def square(number: int) -> int: return number*number async def main() -> None: x = await square(10) print(f'x={x}') y = await square(5) print(f'y={y}') print(f'total={x+y}') if __name__ == '__main__': asyncio.run(main())
Выход:
x=100 y=25 total=125
Как это работает (мы сосредоточимся на функции main()):
- Сначала вызовите сопрограмму Square(), используя ключевое слово await. Ключевое слово await приостановит выполнение сопрограммы main(), дождется завершения сопрограммы Square() и вернет результат:
x = await square(10) print(f'x={x}')
- Во-вторых, вызовите сопрограмму Square() второй раз, используя ключевое слово await:
y = await square(5) print(f'y={y}')
- В-третьих, отобразите сумму:
print(f'total={x+y}')
- Следующий оператор использует функцию run() для выполнения сопрограммы main() и управления циклом событий:
asyncio.run(main())
До сих пор наша программа выполнялась как синхронная программа. Это не раскрывает возможности модели асинхронного программирования.