8.10. Inväntbara klasser¶
8.10.1. Vad en inväntbar är¶
När en coroutine skriver await x frågar språket x hur man väntar på det. Varje objekt som kan svara på det är inväntbart. Det finns två sorter:
Coroutine-objekten som
async def-funktioner returnerar. Att anropasend_request()producerar ett av dessa varje gång – coroutine-objektet, inte resultatet av funktionen. Att skrivaawait send_request()är vad som faktiskt kör kroppen.Objekt av en klass som definierar en
__await__-metod.async defär förkortningen för den första sorten;__await__är det manuella sättet att skapa den andra sorten.
Applikationskod använder den första sorten som standard. Den andra sorten finns för det sällsynta fallet där objektet självt behöver vara det som anroparen awaitar, inte resultatet av att anropa en metod på det.
8.10.2. De två formerna sida vid sida¶
Samma logik skriven som en coroutine och som en inväntbar klass:
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
Båda kan awaitas på samma sätt:
async def main():
a = await yield_then_return(1)
b = await YieldThenReturn(2)
print(a, b) # prints: 1 2
async def-formen är kortare, läses som vanlig Python och låter språket sköta all bokföring av att pausa och återuppta. Klassformen får inget extra i detta exempel.
8.10.3. När klassformen gör sig förtjänt av sin plats¶
När objektet måste vara något som applikationen skickar runt – inte en funktion som anroparen anropar för att få en coroutine – är klassformen det enda alternativet:
Ett future-liknande värde som applikationen skapar, lämnar till en producent och
awaitar senare för att hämta det producerade resultatet.En anpassad primitiv byggd ovanpå en befintlig där det naturliga API:et är
await my_thingsnarare änawait my_thing.wait(). Själva klassenasyncio.Taskär ett inbyggt exempel – att skrivaawait taskfungerar eftersomTaskdefinierar__await__.
För allt som passar formen anropa en funktion, få en coroutine, invänta den vinner async def.
8.10.4. Protokolldetaljerna¶
__await__ är en vanlig metod (inte async def) som returnerar en iterator. Att skriva den som en generator – genom att inkludera minst ett yield i kroppen – gör den automatiskt till en. Varje yield pausar den coroutine som awaitar objektet och lämnar tillbaka kontrollen till händelseloopen; generatorn återupptas nästa gång loopen schemalägger uppgiften. Ett ensamt return (eller att nå slutet) avslutar väntan; vad än return producerar blir värdet av await-uttrycket.
I praktiken lämnar den sällsynta klass som gör detta över själva pausningen till en befintlig inväntbar snarare än att driva loopen själv:
class WaitForEvent:
def __init__(self, event):
self._event = event
def __await__(self):
yield from self._event.wait().__await__()
return self._event
yield from återanvänder den underliggande händelsens __await__ för själva pausningen. De flesta asyncio-applikationer behöver aldrig skriva någon av formerna.