8.10. מחלקות הניתנות להמתנה (awaitable)¶
8.10.1. מהו אובייקט awaitable¶
כאשר קורוטינה כותבת await x, השפה שואלת את x כיצד להמתין לו. כל אובייקט שיודע לענות הוא awaitable. ישנם שני סוגים:
אובייקטי הקורוטינה שפונקציות
async defמחזירות. קריאה ל-send_request()מייצרת אחד מאלה בכל פעם – את אובייקט הקורוטינה, לא את תוצאת הפונקציה. כתיבתawait send_request()היא מה שמריץ בפועל את גוף הפונקציה.אובייקטים של מחלקה שמגדירה מתודת
__await__.async defהוא קיצור הדרך לסוג הראשון;__await__הוא הדרך הידנית ליצור את הסוג השני.
קוד האפליקציה משתמש בסוג הראשון כברירת מחדל. הסוג השני קיים למקרה הנדיר שבו האובייקט עצמו צריך להיות הדבר שהקורא awaits עליו, ולא תוצאת קריאה למתודה עליו.
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
ניתן לכתוב awaited על שתיהן באותו אופן:
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.
בפועל, המחלקה הנדירה שעושה זאת מעבירה את ה-yield לאובייקט 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 לעולם אינן זקוקות לכתוב אף אחת מהצורות.