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 ให้
หน้า frame capture แสดงวิธีทำให้ csi.CSI.snapshot() เป็นมิตรกับ await; เมื่อได้ wrapper นั้นมาแล้ว เนื้อหาของบล็อก async with Spotlight(led): โดยทั่วไปจะเป็น capture loop ที่ทำงานภายใต้แสงสว่างที่สม่ำเสมอ