8.12. Aszinkron környezetkezelők#

A Python áttekintés bemutatta a környezetkezelőket – azokat az objektumokat, amelyeket a with blokkok vezérelnek, ahol az __enter__ a belépéskor fut, az __exit__ pedig a kilépéskor, függetlenül attól, hogyan ér véget a blokk. Az aszinkron környezetkezelők ugyanaz az ötlet áthelyezve az asyncio világába: az __aenter__ és az __aexit__ korutinok, így a beállítás vagy a tisztítás await-tel várhat valamire. Az őket vezérlő blokk az async with.

Egyet már használtunk is. Az asyncio.Lock egy aszinkron környezetkezelő; a zárak oldal async with bus_lock: ... formában használta. Ez az oldal arról szól, hogyan írjunk egyet egy alkalmazás saját erőforrásához.

8.12.1. Mikor írjunk egyet#

Amikor az alkalmazásnak van egy erőforrása, amely párosított beállítást és lebontást igényel, és legalább az egyik oldalnak await-tel várnia kell valamire. Hálózati kapcsolatok, érzékelők, amelyeknek a konfigurálás után stabilizálódási késleltetés kell, bármi, ami belépéskor lezár valamit és kilépéskor feloldja. Az egyszerű szinkron with forma nem alkalmazható, mert az __enter__ és az __exit__ nem lehet korutin.

8.12.2. A két metódus#

  • Az async def __aenter__(self) akkor fut, amikor belépünk a blokkba. Amit visszaad, azt köti az async with opcionális as name ága. A self visszaadása a leggyakoribb forma, de bármilyen érték működik.

  • Az async def __aexit__(self, exc_type, exc, tb) akkor fut, amikor kilépünk a blokkból. Az exc_type None normál kilépéskor; kivétel (vagy megszakítás) esetén ez a kivétel osztálya, az exc pedig a példány. Egy igaz érték visszaadása azt mondja a Pythonnak, hogy a kivétel kezelve lett, és nem szabad továbbterjednie. A None visszaadása (a szokásos eset) hagyja, hogy a kivétel a tisztítás lefutása után tovább terjedjen a hívási láncon.

8.12.3. Egy kidolgozott példa#

Egy reflektor-burkoló, amely a blokk törzsének idejére bekapcsol egy LED-et, egy rövid stabilizálódási késleltetéssel, hogy a megvilágítás stabil legyen, mielőtt bármilyen felvétel megtörténne, majd a kilépéskor ismét kikapcsolja a LED-et:

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())

Amikor belépünk a blokkba, lefut az __aenter__: a LED bekapcsol, az 50 ms-os stabilizálódási await átadja a vezérlést a huroknak, hogy más korutinok közben haladhassanak, és a blokk törzse a várakozás befejeztével indul el. Amikor a blokk véget ér – normál kilépéskor, kivétel esetén, vagy megszakításkor –, lefut az __aexit__, és a LED ismét kikapcsol. A tisztítás minden esetben lefut; ezt a garanciát nyújtja az async with.

A képkocka-felvételi oldal bemutatja, hogyan tehetjük a csi.CSI.snapshot() metódust await-baráttá; ha ez a burkoló a kezünkben van, egy async with Spotlight(led): blokk törzse tipikusan egy felvételi hurok lenne, amely az állandó megvilágítás alatt fut.