8.12. Asynchroniczne menedżery kontekstu¶
Przegląd Pythona wprowadził menedżery kontekstu – obiekty sterowane przez bloki with, w których __enter__ wykonuje się podczas wejścia, a __exit__ podczas wyjścia, niezależnie od tego, jak blok się kończy. Asynchroniczne menedżery kontekstu to ta sama idea przeniesiona do asyncio: __aenter__ i __aexit__ są korutynami, więc konfiguracja lub sprzątanie może coś await-ować. Blokiem, który nimi steruje, jest async with.
Już jednego użyliśmy. asyncio.Lock jest asynchronicznym menedżerem kontekstu; strona blokady używała go jako async with bus_lock: .... Ta strona dotyczy napisania własnego menedżera dla zasobu aplikacji.
8.12.1. Kiedy go napisać¶
Gdy aplikacja ma zasób wymagający sparowanej konfiguracji i likwidacji, a co najmniej jedna z tych stron musi coś await-ować. Połączenia sieciowe, sensory wymagające opóźnienia na ustabilizowanie po konfiguracji, wszystko, co blokuje coś przy wejściu i odblokowuje przy wyjściu. Zwykła synchroniczna postać with nie ma tu zastosowania, ponieważ jej __enter__ i __exit__ nie mogą być korutynami.
8.12.2. Dwie metody¶
async def __aenter__(self)wykonuje się przy wejściu do bloku. To, co zwraca, jest tym, do czego dowiązuje się opcjonalna klauzulaas namewasync with. Zwracanieselfjest najczęstszą postacią, ale dowolna wartość działa.async def __aexit__(self, exc_type, exc, tb)wykonuje się przy wyjściu z bloku.exc_typejest równeNoneprzy normalnym wyjściu; przy wyjątku (lub anulowaniu) jest klasą wyjątku, aexcjest jego instancją. Zwrócenie wartości prawdziwościowej mówi Pythonowi, że wyjątek został obsłużony i nie powinien się propagować. ZwrócenieNone(zwykły przypadek) pozwala wyjątkowi kontynuować w górę łańcucha wywołań po wykonaniu sprzątania.
8.12.3. Przykład w praktyce¶
Wrapper reflektora, który włącza diodę LED na czas trwania bloku, z krótkim opóźnieniem na ustabilizowanie, aby oświetlenie było stabilne przed jakimkolwiek przechwytywaniem, i ponownie wyłącza diodę LED przy wyjściu:
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())
Gdy blok jest wejściowy, wykonuje się __aenter__: dioda LED się zapala, await na 50 ms ustabilizowanie oddaje sterowanie pętli, aby inne korutyny mogły w międzyczasie robić postępy, a ciało bloku rozpoczyna się po zakończeniu oczekiwania. Gdy blok się kończy – przy normalnym wyjściu, przy wyjątku lub przy anulowaniu – wykonuje się __aexit__ i dioda LED ponownie gaśnie. Sprzątanie wykonuje się w każdym przypadku; to jest gwarancja zapewniana przez 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.