Crearea Managerilor de Context cu contextlib (@contextmanager) în Python

Funcții Toate versiunile Python 3

Ghid complet pentru crearea managerilor de context prin generatoare cu decoratorul @contextmanager: eliberarea automată a resurselor, gestionarea tranzacțiilor și contextlib.suppress.

Sintaxă
from contextlib import contextmanager
@contextmanager
def cronometru():
    start = time.perf_counter()
    yield
    print(time.perf_counter() - start)

Protocolul de gestionare a contextului (instrucțiunea with) este mecanismul consacrat prin care Python garantează că resursele alocate (fișiere, conexiuni la baze de date, lock-uri de thread-uri) sunt eliberate deterministic. Scrierea unei clase complete cu metodele dunder __enter__ și __exit__ poate fi însă redundantă. Decoratorul @contextmanager din modulul contextlib permite transformarea oricărei funcții generatoare simple într-un manager de context complet și robust.

Crearea unui cronometru de performanță cu @contextmanager
import time
from contextlib import contextmanager

@contextmanager
def cronometreaza(nume_sarcina: str):
    start = time.perf_counter()
    print(f"[{nume_sarcina}] Start execuție...")
    try:
        # Punctul 'yield' transmite controlul blocului de cod din interiorul 'with':
        yield
    finally:
        # Codul de după yield se execută GARANTAT la ieșirea din bloc,
        # chiar dacă în interior a apărut o excepție!
        durata = time.perf_counter() - start
        print(f"[{nume_sarcina}] Finalizat în {durata:.4f} secunde.")

# Utilizare practică:
with cronometreaza("Calcul matricial"):
    suma = sum(i ** 2 for i in range(1_000_000))

Returnarea unei Resurse din clauza yield

Dacă doriți ca managerul de context să furnizeze o resursă în variabila definită prin clauza as (cum face open() as fisier), transmiteți valoarea ca argument al instrucțiunii yield valoare:

Gestionarea unei tranzacții de baze de date cu rollback automat
@contextmanager
def tranzactie_sigura(conexiune):
    cursor = conexiune.cursor()
    cursor.execute("START TRANSACTION;")
    try:
        yield cursor
        conexiune.commit()
        print("Tranzacție salvată cu succes (COMMIT).")
    except Exception as eroare:
        conexiune.rollback()
        print(f"Tranzacție anulată (ROLLBACK) din cauza: {eroare}")
        raise # Repropagăm eroarea după rollback!
    finally:
        cursor.close()

# Utilizare:
# with tranzactie_sigura(db) as c:
#     c.execute("UPDATE solduri SET total = total - 100 WHERE id = 1;")

Utilitare Rapide din Modulul contextlib

  • contextlib.suppress(*exceptions): Ignoră silențios excepțiile specificate, eliminând blocurile verbos de try ... except: pass:
    from contextlib import suppress
    # Șterge fișierul dacă există, dar nu aruncă eroare dacă lipsește:
    with suppress(FileNotFoundError):
        os.remove("fisier_temporar.txt")
  • contextlib.nullcontext(val): Un manager de context de tip no-op (nu face nimic), util când doriți ca un parametru opțional de context manager să poată fi pasat fără verificare if ctx is not None.