TGStat
TGStat
Введите текст для поиска
Расширенный поиск каналов
  • Язык сайта
    flag Russian flag English flag Uzbek
  • Вход на сайт
  • Каталог
    Каталог каналов и чатов Региональные подборки Тематические подборки Платные каналы Поиск каналов
    Добавить канал/чат
  • Рейтинги
    Рейтинг каналов Рейтинг чатов Рейтинг публикаций
    Рейтинги брендов и персон
  • Аналитика
  • Поиск по публикациям
  • Мониторинг Telegram
  • Продвижение
    Реклама через Яндекс Бизнес Реклама в каналах через TGStat Agency Реклама на сайте TGStat.ru
Находки в опенсорсе

12 Nov 2024, 19:24

Открыть в Telegram Поделиться Пожаловаться

Чем корутина реально отличается от генератора?

Достаточно часто можно услышать, что корутины произошли от генераторов. Живы еще те динозавры, которые помнят @asyncio.coroutine в py3.4 и корутины на yield

Определения

На самом деле, определить, что такое генератор и корутина - не так просто, как может показаться. Я пользуюсь следующими определениям:
- генератор = что-то, что соответствует интерфейсу _collections_abc.Generator
- корутина = что-то, что соответствует интерфейсу _collections_abc.Coroutine

(да, вы можете создавать свои нестандартные генераторы и корутины на Python и C)

Уже в самих интерфейсах, вы можете увидеть, что часть методов идентична, часть отличается:


>>> set(dir(gen)) - set(dir(coro))
{'gi_running', '__next__', 'gi_frame', 'gi_yieldfrom', 'gi_suspended', '__iter__', 'gi_code'}

>>> set(dir(coro)) - set(dir(gen))
{'cr_running', '__await__', 'cr_frame', 'cr_suspended', 'cr_origin', 'cr_code', 'cr_await'}


Копнем чуть глубже, создадим "generator function" и "coroutine function" (функции, которые вернут генератор / корутину):


import asyncio

async def coro_func():
await asyncio.sleep(1)

def gen_func():
yield 1


Байткод их создания будет абсотютно идентичным (разница только в именах):


2 LOAD_CONST 0 ()
MAKE_FUNCTION
STORE_NAME 0 ()


Потому что сами объекты функций будут очень похожими, отличаться будут объекты CodeObject, которые создаются компилятором в assemble.c. Проходимся по всем AST нодам в функции и вычисляем, есть ли там yield или yield from:


static int
symtable_visit_expr(struct symtable *st, expr_ty e)
{
switch (e->kind) {
// ...
case Yield_kind:
if (e->v.Yield.value)
VISIT(st, expr, e->v.Yield.value);
st->st_cur->ste_generator = 1; // теперь функция будет "generator function"
if (st->st_cur->ste_comprehension) {
return symtable_raise_if_comprehension_block(st, e);
}
break;
case YieldFrom_kind:
VISIT(st, expr, e->v.YieldFrom.value);
st->st_cur->ste_generator = 1;
if (st->st_cur->ste_comprehension) {
return symtable_raise_if_comprehension_block(st, e);
}
break;
}
}


И для корутины:


// symtable_visit_stmt(struct symtable *st, stmt_ty s)
case AsyncFunctionDef_kind: {
if (!symtable_add_def(st, s->v.AsyncFunctionDef.name, DEF_LOCAL, LOCATION(s)))
return 0;
// ...
st->st_cur->ste_coroutine = 1;

// symtable_visit_expr(struct symtable *st, expr_ty e)
case Await_kind:
if (!allows_top_level_await(st)) {
if (!_PyST_IsFunctionLike(st->st_cur)) {
return PyErr_SetString(PyExc_SyntaxError, "'await' outside function");
}
if (!IS_ASYNC_DEF(st) && st->st_cur->ste_comprehension == NoComprehension) {
return PyErr_SetString(PyExc_SyntaxError, "'await' outside async function");
}
}
VISIT(st, expr, e->v.Await.value);
st->st_cur->ste_coroutine = 1;


И потом уже просто вычисляем флаги для CodeObject:


static int compute_code_flags(compiler *c)
{
if (_PyST_IsFunctionLike(ste)) {
flags |= ...;
if (ste->ste_generator && !ste->ste_coroutine)
flags |= CO_GENERATOR;
if (ste->ste_generator && ste->ste_coroutine)
flags |= CO_ASYNC_GENERATOR;
// ...
}
if (ste->ste_coroutine && !ste->ste_generator) {
flags |= CO_COROUTINE;
}
return flags;
}


Результат:


>>> from inspect import CO_COROUTINE, CO_GENERATOR
>>> gen_func.__code__.co_flags & CO_GENERATOR
32
>>> coro_func.__code__.co_flags & CO_GENERATOR
0
>>> coro_func.__code__.co_flags & CO_COROUTINE
128


Что происходит при вызове?

Если сделать dis функций gen_func и coro_func, то у них первым байткодом будет RETURN_GENERATOR (см _Py_MakeCoro):
cpython/Lib/_collections_abc.py at 0ef84b0e2bf511b2cb5268a9ce64d7f2209fb3c4 · python/cpython
The Python programming language. Contribute to python/cpython development by creating an account on GitHub.

6.1k 2 47 4 36
Каталог
Каталог каналов и чатов Подборки каналов Поиск каналов Добавить канал/чат
Рейтинги
Рейтинг каналов Telegram Рейтинг чатов Telegram Рейтинг публикаций Рейтинги брендов и персон
API
API статистики API поиска публикаций API Callback
Наши каналы
@TGStat @TGStat_Chat @telepulse @TGStatAPI
Почитать
Академия TGStat Исследование Telegram 2019 Исследование Telegram 2021 Исследование Telegram 2023
Контакты
Справочный центр Поддержка Почта Вакансии
Всякая всячина
Пользовательское соглашение Политика конфиденциальности Публичная оферта
Наши боты
@TGStat_Bot @SearcheeBot @TGAlertsBot @tg_analytics_bot @TGStatChatBot