Lompati ke konten utama
Modul 9 · Pelajaran 3 dari 435/36 dalam kursus~12 min
Pelajaran modul (3/4)

Manajer konteks: dengan dan __enter__/__exit__

Pengelola konteks adalah objek yang Anda gunakan dengan with. Ini menjamin hal itu operasi pengaturan/pembersihan tertentu selalu terjadi, bahkan ketika terjadi kesalahan. Contoh kanonik: membuka dan menutup file.

Pola with

Python
with open("note.txt", "w") as f:
    f.write("ciao")
# qui f è già chiuso, anche se write() ha sollevato un'eccezione

Dalam bahasa Inggris sederhana: "dengan sumber daya X terbuka sebagai f, lakukan ini. Setelah selesai (apa pun yang terjadi), tutup X." Tanpa with Anda harus menulis a try/finally setiap saat.

Lebih dari satu pada satu waktu

Python
with open("a.txt") as f, open("b.txt") as g:
    dati_a = f.read()
    dati_b = g.read()

Menulis manajer konteks Anda sendiri (kelas)

Anda perlu mendefinisikan dua metode dunder: __enter__ dan __exit__.

Python
class Timer:
    def __enter__(self):
        import time
        self.t0 = time.perf_counter()
        return self  # è l'oggetto che finisce dopo "as"
    def __exit__(self, exc_type, exc_value, traceback):
        import time
        self.elapsed = time.perf_counter() - self.t0
        # ritornare True ingoia l'eccezione; di solito non lo vuoi
        return False

with Timer() as t:
    sum(range(10**6))
t.elapsed  # quanto è durato
  • __enter__(self) dipanggil saat memasuki blok; nilai yang dikembalikan adalah apa yang Anda dapatkan setelah as.
  • __exit__(self, exc_type, exc_value, tb) dipanggil saat keluar (selalu). Jika mengembalikan True, pengecualian apa pun ditekan.

Pintasannya: @contextmanager

contextlib.contextmanager memungkinkan Anda menulis pengelola konteks sebagai a fungsi generator:

Python
from contextlib import contextmanager

@contextmanager
def timer():
    import time
    t0 = time.perf_counter()
    try:
        yield  # qui sta il blocco "with"
    finally:
        elapsed = time.perf_counter() - t0
        print(f"durata: {elapsed:.4f}s")

with timer():
    sum(range(10**6))

Kode sebelum yield adalah __enter__, kode setelah adalah __exit__. try/finally menjamin pembersihan bahkan ketika terjadi kesalahan.

menekan: mengabaikan pengecualian

Python
from contextlib import suppress

with suppress(FileNotFoundError):
    open("forse-non-esiste.txt").read()
# se il file non c'è, nessun errore propaga

Kesalahan: with hanya untuk objek yang mendukungnya

Python
x = 42
with x:  # AttributeError: __enter__
    ...

Menulis pengelola konteks dengan @contextmanager

Modul contextlib perpustakaan standar mengekspor dekorator yang sangat nyaman disebut @contextmanager untuk membuat pengelola konteks menggunakan fungsi generator, alih-alih menulis kelas verbose yang menerapkan metode __enter__ dan __exit__. Anda cukup menulis fungsi generator yang menggunakan satu yield:

Python
from contextlib import contextmanager

@contextmanager
def manage_resource():
    # __enter__ code
    yield resource
    # __exit__ code

Cobalah sendiri

Latihan#python.m9.l3.e1
Upaya: 0Memuat…

Gunakan kontekslib.suppress untuk mencoba int('non-numero') mengabaikan ValueError. Setelah blok tetapkan `ok = True`. Evaluasi `oke`.

Memuat editor…
Tunjukkan petunjuk

supres(ValueError) hanya menelan ValueError.

Solusi tersedia setelah 3 upaya

Tinjau latihan

Latihan#python.m9.l3.e2
Upaya: 0Memuat…

Tulis manajer konteks `kumpulkan` dengan @contextmanager yang menghasilkan daftar kosong dan, saat keluar, ubah menjadi tuple (cetak). Gunakan untuk menambahkan 1, 2, 3 ke daftar di dalam blok. Pada akhirnya, evaluasi `len(koleksi)` di mana `koleksi` adalah daftar yang dihasilkan.

Memuat editor…
Tunjukkan petunjuk

hasilkan item untuk mengekspos daftar di dalam blok with.

Solusi tersedia setelah 3 upaya

Tantangan tambahan

Latihan#python.m9.l3.e3
Upaya: 0Memuat…

Write a custom context manager using the `@contextmanager` decorator from `contextlib`. The context manager must be named `mock_session`. Upon entry, it must append `'start'` to a global list `log_list = []`, yield control using `yield`, and upon exit append `'stop'`. Finally, evaluate `log_list` after using the context manager with `with mock_session():`.

Memuat editor…
Tunjukkan petunjuk

Gunakan hasil di dalam mock_session untuk menunda eksekusi sebelum menambahkan 'berhenti'.

Solusi tersedia setelah 3 upaya