8.12. Async context managers

Python Overview ได้แนะนำ context managers -- อ็อบเจกต์ที่บล็อก with ขับเคลื่อน โดย __enter__ ทำงานขาเข้าและ __exit__ ทำงานขาออกไม่ว่าบล็อกจะสิ้นสุดอย่างไร Async context managers เป็นแนวคิดเดียวกันที่ย้ายเข้าสู่ asyncio: __aenter__ และ __aexit__ เป็น coroutines ดังนั้นการตั้งค่าหรือการล้างข้อมูลสามารถ await บางอย่างได้ บล็อกที่ขับเคลื่อนคือ async with

เราได้ใช้มันแล้วครั้งหนึ่ง asyncio.Lock เป็น async context manager หน้า locks ใช้มันในรูปแบบ async with bus_lock: ... หน้านี้เกี่ยวกับการเขียน async context manager สำหรับทรัพยากรของแอปพลิเคชันเอง

8.12.1. เมื่อไรควรเขียน

เมื่อแอปพลิเคชันมีทรัพยากรที่ต้องการการตั้งค่าและการล้างข้อมูลที่ จับคู่กัน และอย่างน้อยหนึ่งฝั่งต้อง await บางอย่าง เช่น การเชื่อมต่อเครือข่าย เซนเซอร์ที่ต้องการระยะเวลา settling หลังการกำหนดค่า หรืออะไรก็ตามที่ล็อคบางอย่างเมื่อเข้าและปลดล็อคเมื่อออก รูปแบบ with แบบ synchronous ปกติใช้ไม่ได้เพราะ __enter__ และ __exit__ ไม่สามารถเป็น coroutines ได้

8.12.2. สองเมธอด

  • async def __aenter__(self) ทำงานเมื่อเข้าบล็อก สิ่งที่มันคืนค่าคือสิ่งที่ประโยค as name ของ async with ผูกกับ การคืน self เป็นรูปแบบที่พบบ่อยที่สุด แต่ค่าใดก็ได้

  • async def __aexit__(self, exc_type, exc, tb) ทำงานเมื่อออกจากบล็อก exc_type เป็น None เมื่อออกปกติ เมื่อมีข้อยกเว้น (หรือการยกเลิก) จะเป็น class ของข้อยกเว้น และ exc คือ instance การคืนค่าที่เป็น truthy บอก Python ว่าข้อยกเว้นได้รับการ จัดการ แล้วและไม่ควรกระจายต่อ การคืน None (กรณีปกติ) ให้ข้อยกเว้นดำเนินต่อขึ้น call chain หลังจากการล้างข้อมูลทำงาน

8.12.3. ตัวอย่างที่ทำงานได้จริง

wrapper ของ spotlight ที่เปิด LED สำหรับส่วนเนื้อหาของบล็อก พร้อม settling delay สั้นๆ เพื่อให้แสงสว่างคงที่ก่อนที่จะมีการถ่ายภาพ และปิด 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 settle 50 ms ยึดควบคุมให้กับลูปเพื่อให้ coroutine อื่นทำงานได้ในระหว่างนั้น และเนื้อหาบล็อกเริ่มเมื่อการรอเสร็จสิ้น เมื่อบล็อกสิ้นสุด -- ไม่ว่าจะออกปกติ มีข้อยกเว้น หรือถูกยกเลิก -- __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.