8.10. Ожидаемые классы¶
8.10.1. Что такое ожидаемый объект¶
Когда корутина пишет await x, язык спрашивает у x, как его ожидать. Любой объект, который знает, как ответить, является ожидаемым. Существует два вида:
Объекты-корутины, которые возвращают функции
async def. Вызовsend_request()каждый раз создаёт один из них – объект корутины, а не результат функции. Записьawait send_request()– это то, что на самом деле запускает тело.Объекты класса, который определяет метод
__await__.async def– это сокращённая запись для первого вида;__await__– это ручной способ создать второй вид.
Прикладной код по умолчанию использует первый вид. Второй вид существует для редкого случая, когда сам объект должен быть тем, что вызывающая сторона awaits, а не результатом вызова метода у него.
8.10.2. Две формы рядом¶
Одна и та же логика, написанная как корутина и как ожидаемый класс:
import asyncio
# async def -- almost always the right choice
async def yield_then_return(value):
await asyncio.sleep_ms(0)
return value
# awaitable class -- equivalent, rarely written
class YieldThenReturn:
def __init__(self, value):
self._value = value
def __await__(self):
yield # let the loop run once
return self._value # value of the ``await`` expression
Обе можно awaitить одинаковым образом:
async def main():
a = await yield_then_return(1)
b = await YieldThenReturn(2)
print(a, b) # prints: 1 2
Форма async def короче, читается как обычный Python и позволяет языку управлять всем учётом приостановки и возобновления. Форма с классом не даёт в этом примере ничего дополнительного.
8.10.3. Когда форма с классом оправдана¶
Когда объект должен быть чем-то, что приложение передаёт по кругу – а не функцией, которую вызывающая сторона вызывает, чтобы получить корутину – форма с классом является единственным вариантом:
Future-подобное значение, которое приложение создаёт, передаёт производителю и
awaits позже, чтобы получить произведённый результат.Пользовательский примитив, построенный поверх существующего, где естественным API является
await my_thing, а неawait my_thing.wait(). Сам классasyncio.Task– встроенный пример: записьawait taskработает, потому чтоTaskопределяет__await__.
Для всего, что подходит под форму вызвать функцию, получить корутину, ожидать её, побеждает async def.
8.10.4. Детали протокола¶
__await__ – это обычный метод (не async def), который возвращает итератор. Если написать его как генератор – включив хотя бы один yield в тело – он автоматически станет итератором. Каждый yield приостанавливает корутину, которая awaits объект, и возвращает управление циклу событий; генератор возобновляется в следующий раз, когда цикл планирует задачу. Простой return (или выход за конец) завершает ожидание; то, что производит return, становится значением выражения await.
На практике редкий класс, который это делает, передаёт выдачу значений существующему ожидаемому объекту, а не управляет циклом сам:
class WaitForEvent:
def __init__(self, event):
self._event = event
def __await__(self):
yield from self._event.wait().__await__()
return self._event
yield from повторно использует __await__ нижележащего события для фактической приостановки. Большинству asyncio-приложений никогда не нужно писать ни одну из этих форм.