В чем разница между async def и def в Python?

Ответ

Ключевое различие — в модели выполнения: def создает синхронные функции, а async def — асинхронные корутины (coroutine).

def (Синхронная функция)

  • Выполнение: Блокирующее и последовательное. Если функция выполняет долгую операцию (например, сетевой запрос), она блокирует весь поток до своего завершения.
  • Вызов: Обычный прямой вызов: result = my_function().

async def (Асинхронная корутина)

  • Выполнение: Неблокирующее. Может приостановить свое выполнение в точке await, передав управление обратно циклу событий (event loop). Это позволяет выполнять другие задачи, пока корутина ждет завершения долгой I/O-операции (например, ответа от сервера).
  • Вызов: Требует await внутри другой async функции: result = await my_coroutine(). Простой вызов my_coroutine() вернет объект корутины, но не выполнит ее код.

Когда что использовать:

  • def: Для CPU-bound задач (математические вычисления, обработка данных в памяти) и простого последовательного кода.
  • async def: Для I/O-bound задач (сетевые запросы, работа с файлами, базами данных), где программа большую часть времени ожидает ответа от внешних ресурсов.

Пример:

import asyncio
import time

# Синхронная функция (блокирующая)
def sync_task():
    print("Начало синхронной задачи...")
    time.sleep(1) # Блокирует поток на 1 секунду
    print("...синхронная задача завершена")
    return {"data": "sync_data"}

# Асинхронная функция (неблокирующая)
async def async_task():
    print("Начало асинхронной задачи...")
    await asyncio.sleep(1) # Не блокирует, передает управление
    print("...асинхронная задача завершена")
    return {"data": "async_data"}

async def main():
    # Вызов корутины с помощью await
    result = await async_task()
    print(result)

# Для запуска асинхронного кода
# asyncio.run(main())

Ответ 18+ 🔞

А, слушай, вот тебе история про двух братьев-близнецов в Python, def и async def. С виду похожи, а по сути — хуй с горы, два разных зверя.

Вот представь, есть у тебя обычный def. Мужик как мужик. Солидный, последовательный. Скажешь ему: «Сходи, принеси пива». Он пошёл, и пока не вернётся с банкой в руках, ты нихуя другого делать не будешь. Стоишь и ждёшь, как лох. Это блокирующее выполнение. Если там, внутри, запрос к базе данных или сайту, который тормозит как черепаха в сиропе, то весь твой поток — в анабиозе, пока операция не завершится. Вызывается просто: result = my_function(). Всё.

А теперь его брат-шизик, async def. Это не функция, а корутина, ёпта. Хитрый такой. Ты ему: «Сходи, принеси пива». Он говорит: «Ага, щас». Дошёл до холодильника, а там дверь не открывается сразу. Вместо того чтобы биться лбом, он говорит: «Окей, я тут на await повисну, пока холодильник не откроется, а ты, event loop, иди другие дела делай!». И управление возвращается циклу событий, который может в это время другую корутину погонять. Холодильник открылся — корутина «просыпается» и таки приносит пиво. Это неблокирующее выполнение. Вызвать его просто так — my_coroutine() — нельзя, получишь просто объект-обещание. Надо сказать await: result = await my_coroutine().

Короче, когда что юзать:

  • def — когда твоя задача CPU-bound, то есть жрёт процессор: вычисления, сортировки, шифрование. Или когда код простой, линейный, и париться неохота.
  • async def — когда задача I/O-bound, то есть больше ждёт, чем работает: запросы в сеть (API, сайты), чтение/запись файлов, общение с базами данных. Тут асинхронность выжмет все соки из скорости, потому что пока одна корутина ждёт ответа от сервера, другая уже может следующий запрос готовить.

Смотри, как это выглядит в коде, тут всё по-честному:

import asyncio
import time

# Обычный дед-блокировщик
def sync_task():
    print("Начало синхронной задачи...")
    time.sleep(1) # Всё, пиздец, поток уснул на секунду
    print("...синхронная задача завершена")
    return {"data": "sync_data"}

# Асинхронный пройдоха
async def async_task():
    print("Начало асинхронной задачи...")
    await asyncio.sleep(1) # А я, блядь, подожду, но других не буду тормозить!
    print("...асинхронная задача завершена")
    return {"data": "async_data"}

async def main():
    # Работаем с корутиной правильно - через await
    result = await async_task()
    print(result)

# Чтобы этот цирк запустить, нужен asyncio.run(main())

Вот и вся магия. Выбирай инструмент по задаче, а то получится, как с молотком и микроскопом.