8.10. Awaitable osztályok¶
8.10.1. Mi az awaitable¶
Amikor egy korutin await x-et ír, a nyelv megkérdezi x-től, hogyan kell rá várni. Bármely objektum, amely tudja, hogyan válaszoljon, awaitable. Két fajta van:
Azok a korutinobjektumok, amelyeket az
async deffüggvények adnak vissza. Asend_request()hívása minden alkalommal ezek egyikét állítja elő – a korutinobjektumot, nem a függvény eredményét. Azawait send_request()írása az, ami valójában lefuttatja a törzset.Egy olyan osztály objektumai, amely definiál egy
__await__metódust. Azasync defaz első fajta rövidítése; az__await__a kézi módja a második fajta létrehozásának.
Az alkalmazáskód alapértelmezetten az első fajtát használja. A második fajta arra a ritka esetre létezik, amikor magának az objektumnak kell annak lennie, amire a hívó await-tel vár, nem pedig egy rajta meghívott metódus eredményének.
8.10.2. A két forma egymás mellett¶
Ugyanaz a logika korutinként és awaitable osztályként megírva:
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
Mindkettő ugyanúgy await-elhető:
async def main():
a = await yield_then_return(1)
b = await YieldThenReturn(2)
print(a, b) # prints: 1 2
Az async def forma rövidebb, úgy olvasható, mint a normál Python, és lehetővé teszi, hogy a nyelv kezelje az összes felfüggesztési és folytatási könyvelést. Az osztályforma ebben a példában semmi extrát nem nyújt.
8.10.3. Mikor érdemes az osztályformát választani¶
Amikor az objektumnak lennie kell valaminek, amit az alkalmazás körbeadogat – nem pedig egy függvénynek, amelyet a hívó meghív, hogy korutint kapjon –, az osztályforma az egyetlen lehetőség:
Egy future-szerű érték, amelyet az alkalmazás létrehoz, átad egy termelőnek, majd később
await-tel vár rá, hogy lekérje az előállított eredményt.Egy meglévő primitívre épülő egyedi primitív, ahol a természetes API az
await my_thing, nem pedig azawait my_thing.wait(). Maga azasyncio.Taskosztály egy beépített példa – azawait taskírása azért működik, mert aTaskdefiniálja az__await__-et.
Bármire, ami a hívj meg egy függvényt, kapj egy korutint, várj rá formába illik, az async def a nyerő.
8.10.4. A protokoll részletei¶
Az __await__ egy hagyományos metódus (nem async def), amely egy iterátort ad vissza. Ha generátorként írjuk meg – legalább egy yield-et téve a törzsbe –, az automatikusan azzá teszi. Minden yield felfüggeszti az objektumra await-tel váró korutint, és visszaadja a vezérlést az eseményhuroknak; a generátor akkor folytatódik, amikor a hurok legközelebb ütemezi a feladatot. Egy önálló return (vagy a végére érés) befejezi a várakozást; amit a return előállít, az lesz az await kifejezés értéke.
A gyakorlatban az a ritka osztály, amely ezt teszi, a yield-elést egy meglévő awaitable-re bízza, ahelyett hogy maga működtetné a hurkot:
class WaitForEvent:
def __init__(self, event):
self._event = event
def __await__(self):
yield from self._event.wait().__await__()
return self._event
A yield from újrahasznosítja a mögöttes esemény __await__-jét a tényleges felfüggesztéshez. A legtöbb asyncio-alkalmazásnak soha nincs szüksége egyik forma megírására sem.