8.12. מנהלי הקשר אסינכרוניים

סקירת Python הציגה מנהלי הקשר – האובייקטים שבלוקי with מניעים, כאשר __enter__ רץ בדרך פנימה ו-__exit__ רץ בדרך החוצה ללא קשר לאופן שבו הבלוק מסתיים. מנהלי הקשר אסינכרוניים הם אותו רעיון שהועבר לתוך asyncio: __aenter__ ו-__aexit__ הם קורוטינות, כך שההתקנה או הניקוי יכולים await למשהו. הבלוק שמניע אותם הוא async with.

כבר השתמשנו באחד. asyncio.Lock הוא מנהל הקשר אסינכרוני; עמוד מנעולים השתמש בו כ-async with bus_lock: .... עמוד זה עוסק בכתיבת אחד עבור משאב משלו של יישום.

8.12.1. מתי לכתוב אחד

כשליישום יש משאב הזקוק להתקנה ופירוק מזווגים, ולפחות אחד מהצדדים הללו צריך await למשהו. חיבורי רשת, חיישנים הזקוקים להשהיית התייצבות לאחר תצורה, כל דבר שנועל משהו בכניסה ופותח אותו ביציאה. צורת ה-with הסינכרונית הפשוטה אינה ישימה מפני שה-__enter__ וה-__exit__ שלה אינם יכולים להיות קורוטינות.

8.12.2. שתי המתודות

  • async def __aenter__(self) רץ כשנכנסים לבלוק. כל מה שהוא מחזיר הוא מה שסעיף ה-as name האופציונלי של async with נקשר אליו. החזרת self היא הצורה הנפוצה ביותר, אך כל ערך עובד.

  • async def __aexit__(self, exc_type, exc, tb) רץ כשיוצאים מהבלוק. exc_type הוא None ביציאה רגילה; בחריגה (או בביטול) הוא המחלקה של החריגה, ו-exc הוא המופע. החזרת ערך אמיתי אומרת ל-Python שהחריגה טופלה ולא צריכה להתפשט. החזרת None (המקרה הרגיל) מאפשרת לחריגה להמשיך במעלה שרשרת הקריאות לאחר שהניקוי רץ.

8.12.3. דוגמה מעובדת

עוטף זרקור שמדליק LED למשך גוף הבלוק, עם השהיית התייצבות קצרה כך שהתאורה יציבה לפני שמתרחשים צילומים כלשהם, ומכבה את ה-LED שוב בדרך החוצה:

import asyncio
from machine import LED


class Spotlight:
    def __init__(self, led):
        self._led = led

    async def __aenter__(self):
        self._led.on()
        await asyncio.sleep_ms(50)
        return self

    async def __aexit__(self, exc_type, exc, tb):
        self._led.off()

async def main():
    led = LED("LED_WHITE")

    async with Spotlight(led):
        # work that benefits from steady illumination
        await asyncio.sleep_ms(200)

asyncio.run(main())

כשנכנסים לבלוק, __aenter__ רץ: ה-LED נדלק, ה-await של 50 ms להתייצבות מוותר ללולאה כך שקורוטינות אחרות יכולות להתקדם בינתיים, וגוף הבלוק מתחיל ברגע שההמתנה מסתיימת. כשהבלוק מסתיים – ביציאה רגילה, בחריגה, או בביטול – __aexit__ רץ וה-LED נכבה בחזרה. הניקוי רץ בכל מקרה; זוהי הערובה ש-async with מספק.

The frame capture page shows how to make csi.CSI.snapshot() await-friendly; once that wrapper is in hand, the body of an async with Spotlight(led): block would typically be a capture loop running under the steady illumination.