8.10. Awaitable-класи¶
8.10.1. Що таке awaitable¶
Коли корутина пише await x, мова запитує x, як на нього чекати. Будь-який об’єкт, що знає відповідь, є awaitable. Є два види:
Об’єкти корутин, що повертають функції
async def. Кожного разу викликsend_request()створює один з них — об’єкт корутини, а не результат функції. Записawait send_request()— ось що насправді виконує тіло.Об’єкти класу, що визначає метод
__await__.async def— скорочення для першого виду;__await__— ручний спосіб створити другий вид.
За замовчуванням код застосунку використовує перший вид. Другий вид існує для рідкісного випадку, коли сам об’єкт повинен бути тим, чого await-ає викликач, а не результатом виклику методу на ньому.
8.10.2. Дві форми поряд¶
Та сама логіка, написана у вигляді корутини та у вигляді awaitable-класу:
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-подібне значення, яке застосунок створює, передає виробнику та
await-ає пізніше для отримання виробленого результату.Власний примітив, побудований поверх існуючого, де природним API є
await my_thing, а неawait my_thing.wait(). Класasyncio.Taskсам є вбудованим прикладом — записawait taskпрацює, оскількиTaskвизначає__await__.
Для всього, що підходить під форму викликати функцію, отримати корутину, await-нути її, async def перемагає.
8.10.4. Деталі протоколу¶
__await__ — це звичайний метод (не async def), що повертає ітератор. Написання його як генератора — з хоча б одним yield у тілі — автоматично робить його таким. Кожен yield призупиняє корутину, що await-ає об’єкт, і передає керування назад циклу подій; генератор відновлюється наступного разу, коли цикл планує задачу. Голий return (або вихід з кінця) завершує очікування; те, що виробляє return, стає значенням виразу await.
На практиці рідкісний клас, що це робить, передає yielding існуючому awaitable, а не керує циклом безпосередньо:
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 ніколи не потребують написання жодної з цих форм.