8.10. Awaitable klassen¶
8.10.1. Wat een awaitable is¶
Wanneer een coroutine await x schrijft, vraagt de taal aan x hoe erop gewacht moet worden. Elk object dat dat weet te beantwoorden is awaitable. Er zijn twee soorten:
De coroutine-objecten die
async def-functies teruggeven. Het aanroepen vansend_request()produceert er telkens zo een – het coroutine-object, niet het resultaat van de functie. Doorawait send_request()te schrijven wordt de body daadwerkelijk uitgevoerd.Objecten van een klasse die een
__await__-methode definieert.async defis de verkorte schrijfwijze voor de eerste soort;__await__is de handmatige manier om de tweede soort te maken.
Applicatiecode gebruikt standaard de eerste soort. De tweede soort bestaat voor het zeldzame geval waarin het object zelf datgene moet zijn waarop de aanroeper awaitt, en niet het resultaat van het aanroepen van een methode erop.
8.10.2. De twee vormen naast elkaar¶
Dezelfde logica, geschreven als coroutine en als awaitable klasse:
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
Op beide kan op dezelfde manier awaited worden:
async def main():
a = await yield_then_return(1)
b = await YieldThenReturn(2)
print(a, b) # prints: 1 2
De async def-vorm is korter, leest als normale Python en laat de taal alle administratie van pauzeren en hervatten verzorgen. De klassevorm levert in dit voorbeeld niets extra’s op.
8.10.3. Wanneer de klassevorm zijn plaats verdient¶
Wanneer het object datgene moet zijn dat de applicatie doorgeeft – en niet een functie die de aanroeper aanroept om een coroutine te krijgen – is de klassevorm de enige optie:
Een future-achtige waarde die de applicatie aanmaakt, aan een producent overhandigt en later
awaitt om het geproduceerde resultaat op te halen.Een eigen primitief gebouwd bovenop een bestaande, waarbij de natuurlijke API
await my_thingis in plaats vanawait my_thing.wait(). De klasseasyncio.Taskis zelf een ingebouwd voorbeeld –await taskschrijven werkt omdatTask__await__definieert.
Voor alles dat in de vorm roep een functie aan, krijg een coroutine, await die past, wint async def.
8.10.4. De protocoldetails¶
__await__ is een gewone methode (geen async def) die een iterator teruggeeft. Door het als een generator te schrijven – met ten minste één yield in de body – wordt het er automatisch een. Elke yield pauzeert de coroutine die op het object awaitt en geeft de controle terug aan de event loop; de generator wordt hervat zodra de loop de taak weer inplant. Een kale return (of het bereiken van het einde) beëindigt het wachten; wat de return oplevert wordt de waarde van de await-expressie.
In de praktijk delegeert de zeldzame klasse die dit doet het yielden aan een bestaande awaitable, in plaats van de loop zelf aan te sturen:
class WaitForEvent:
def __init__(self, event):
self._event = event
def __await__(self):
yield from self._event.wait().__await__()
return self._event
De yield from hergebruikt de __await__ van het onderliggende event voor het daadwerkelijke pauzeren. De meeste asyncio-applicaties hoeven nooit een van beide vormen te schrijven.