Belajar dengan membaca secara berurutan

contextlib — Manajemen Resource dan with Kustom

Artikel ini adalah bagian dari Kursus Python, yang membantu Anda menguasai keterampilan yang diperlukan untuk menggunakan Python dari nol, mulai dari dasar hingga tingkat lanjut.
Pelajari @contextmanager plus yield untuk membuat with tanpa class, cleanup pasti via try/finally, dan menelan exception spesifik dengan suppress lewat contoh.

contextlib adalah modul yang harus kamu pakai ketika ingin menulis with statement sendiri. Kita akan membahas dua alat paling sederhananya secara berurutan: @contextmanager untuk cara paling sederhana mendefinisikannya, dan suppress untuk menelan exception tertentu.

@contextmanager — definisikan with statement dengan mudah

with statement Python adalah cara aman untuk menangani "hal-hal yang harus kamu bersihkan setelah dipakai"open file, koneksi DB, akuisisi lock, dan sebagainya. Untuk membuat class bekerja dengan with dengan tangan, kamu perlu mendefinisikan __enter__ / __exit__, yang merupakan banyak boilerplate. @contextmanager adalah jalan pintas: tulis satu generator (fungsi yang berisi yield) dan kamu mendapatkan context manager yang bekerja dengan with. Kode sebelum yield adalah "awal aksi", dan kode setelahnya adalah "cleanup".

struktur @contextmanager
@contextmanagerdef section(name):Setup(sebelum yield)yieldTeardown(setelah yield)
Fungsi generator plus @contextmanager — bagian sebelum yield adalah setara __enter__ (setup), dan bagian setelah yield adalah setara __exit__ (teardown). Value yield adalah yang diikat oleh as di with statement.
from contextlib import contextmanager

# --- Referensi: membangun class yang kompatibel dengan with ----------
# class Section:
#     def __init__(self, name):
#         self.name = name
#     def __enter__(self):
#         print(f"--- {self.name} start ---")
#         return self                 # value yang diikat oleh `as` with
#     def __exit__(self, exc_type, exc, tb):
#         print(f"--- {self.name} end ---")
# ----------------------------------------------------------

# Versi @contextmanager: satu fungsi generator melakukan pekerjaan yang sama
@contextmanager
def section(name):
    print(f"--- {name} start ---")
    yield                       # tubuh with jalan di sini
    print(f"--- {name} end ---")

# Pemakaian
with section("agregasi"):
    total = sum(range(100))
    print("Total:", total)

# Output:
# --- agregasi start ---
# Total: 4950
# --- agregasi end ---
Penyederhanaan versi class → versi @contextmanager
Versi class__init__ / __enter__ /__exit__ — 3 methodSederhanakan dengan@contextmanagerVersi generatorsebelum yield = setupsetelah yield = teardown
Mengganti class dengan __init__ / __enter__ / __exit__ (tiga method) dengan satu fungsi generator yang didekorasi oleh @contextmanager memetakan kode sebelum/setelah yield langsung ke setup/teardown — kode jauh lebih sedikit.

Pakai try/finally untuk menjamin cleanup bahkan saat exception

Kode setelah yield mungkin tidak jalan jika exception dilempar di dalam blok with. Untuk cleanup yang harus selalu terjadi — menutup file, melepas lock — bungkus tubuh generator dalam try: yield finally: cleanup() untuk keamanan.

Bangun transaction(name) yang memodelkan transaksi DB dengan @contextmanager. Cetak BEGIN sebelum dan COMMIT setelah, dengan SQL di dalam blok terlihat di antaranya.

① Impor contextmanager dari contextlib

② Definisikan fungsi transaction(name) yang didekorasi dengan @contextmanager — cetak [name] BEGIN, kemudian yield, kemudian cetak [name] COMMIT setelah

③ Di dalam with transaction("order_create"):, cetak dua baris SQL: INSERT INTO orders (id, total) VALUES (1, 1980) dan UPDATE inventory SET stock = stock - 1

(Jika kode kamu jalan dengan benar, penjelasannya akan muncul.)

Python Editor

Jalankan kode untuk melihat output

suppress — telan exception tertentu

contextlib.suppress(ExceptionType, ...) adalah context manager untuk menelan exception yang ditentukan dan melanjutkan eksekusi. Pola "abaikan saja exception ini dan lanjutkan" — setara dengan try: ... except KeyError: pass — pas dalam satu baris. Kamu bisa mendaftarkan beberapa tipe exception sekaligus, dan apa pun di luar daftar merambat secara normal.

from contextlib import suppress
import os

# "Jalankan dengan nilai default bahkan jika file tidak ada"
config = {"theme": "light", "font_size": 14}
with suppress(FileNotFoundError):
    with open("user_config.txt") as f:
        config["theme"] = f.read().strip()
# Tidak akan crash jika user_config.txt tidak ada — config tetap pada default
print(config)
# → {'theme': 'light', 'font_size': 14}

# "Lewati diam-diam jika sudah dihapus"
with suppress(FileNotFoundError):
    os.remove("old.tmp")
print("Hapus selesai (OK jika tidak ada)")

Pakai suppress untuk terus berjalan bahkan ketika mengambil key yang hilang dari dict.

① Impor suppress dari contextlib

② Definisikan dict user = {"name": "Budi", "email": "budi@example.com"} (catatan: tidak ada key age)

③ Di dalam with suppress(KeyError):, coba akses user["age"] — konfirmasi bahwa key yang hilang tidak crash

④ Tepat setelah blok, cetak Lanjut: tidak crash

⑤ Cetak nama dan email pengguna dalam satu baris sebagai Pengguna: ◯ <◯>

Python Editor

Jalankan kode untuk melihat output
QUIZ

Cek Pemahaman

Jawab setiap pertanyaan satu per satu.

Soal 1Ketika kamu mendekorasi fungsi generator dengan @contextmanager, mengapa ia perlu yield tepat sekali?

Soal 2Di dalam with suppress(KeyError):, apa yang terjadi ketika KeyError dilempar?