Как реализовывались корутины в Python до появления синтаксиса async/await?

Ответ

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

Основными инструментами были:

  1. Декоратор @asyncio.coroutine: Он помечал функцию-генератор как корутину, совместимую с циклом событий asyncio.
  2. Выражение yield from: Использовалось вместо современного await для передачи управления другой корутине и ожидания её результата.

По сути, async def является синтаксическим сахаром над генераторами, а await — над yield from.

Сравнение синтаксиса:

Старый стиль (Python 3.4):

import asyncio

@asyncio.coroutine
def old_style_fetch_data(url):
    print(f"Fetching {url}...")
    # Вместо await используется yield from
    response = yield from asyncio.sleep(1) # Имитация сетевого запроса
    print(f"Done fetching {url}.")
    return {"data": f"Some data from {url}"}

loop = asyncio.get_event_loop()
loop.run_until_complete(old_style_fetch_data("https://example.com"))

Современный стиль (Python 3.5+):

import asyncio

async def new_style_fetch_data(url):
    print(f"Fetching {url}...")
    # Используется async/await
    await asyncio.sleep(1)
    print(f"Done fetching {url}.")
    return {"data": f"Some data from {url}"}

asyncio.run(new_style_fetch_data("https://example.com"))

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

Ответ 18+ 🔞

Да ты посмотри, какая история-то, блядь! Прямо как в том анекдоте про Герасима, только про питон. Раньше-то, сука, чтобы асинхронность запилить, приходилось выкручиваться, как уж на сковородке.

Вот представь: Python 3.4, темные времена, async и await ещё не изобрели, а ебашить асинхронно уже охота. И что делали? Да хуй с горы, использовали генераторы, блядь! Самые обычные yield.

Главными инструментами были два страдальца:

  1. Декоратор @asyncio.coroutine. Этой хуйней обмазывали функцию-генератор, чтобы цикл событий понял — о, это корутина, её можно ебашить.
  2. Выражение yield from. Вместо красивого await приходилось городить эту конструкцию, чтобы передать управление и ждать, пока другая корутина свои дела сделает.

По сути, весь этот новый красивый синтаксис — это просто синтаксический сахар, намазанный на старые добрые генераторы. async def — это обёртка, а await — это yield from, только в цилиндре.

Смотри, как раньше мучались (Python 3.4):

import asyncio

@asyncio.coroutine
def old_style_fetch_data(url):
    print(f"Fetching {url}...")
    # Вместо await используется yield from
    response = yield from asyncio.sleep(1) # Имитация сетевого запроса
    print(f"Done fetching {url}.")
    return {"data": f"Some data from {url}"}

loop = asyncio.get_event_loop()
loop.run_until_complete(old_style_fetch_data("https://example.com"))

Видал? yield from! Прямо как немой Герасим мычит. "Му-му, отдай мне управление, блядь, и жди".

А теперь посмотри, как сейчас красиво (Python 3.5+):

import asyncio

async def new_style_fetch_data(url):
    print(f"Fetching {url}...")
    # Используется async/await
    await asyncio.sleep(1)
    print(f"Done fetching {url}.")
    return {"data": f"Some data from {url}"}

asyncio.run(new_style_fetch_data("https://example.com"))

Во, ёпта! Чисто, понятно, сразу видно — асинхронная функция. А раньше-то вся проблема была, блядь, в том, что корутина с виду ни хуя не отличалась от простого генератора. Запутаться — раз плюнуть. Сейчас же всё явно, как божий день. В рот меня чих-пых, прогресс налицо!