Belajar dengan membaca secara berurutan

Statement with dan Context Manager — Buka/Tutup Aman dengan __enter__ / __exit__

Pelajari statement with Python dan context manager. Pasangan __enter__ / __exit__ untuk buka/tutup aman, dan cara menulisnya sendiri — hands-on.

Sebelumnya kita bekerja melindungi state internal class. Kali ini kita melangkah satu lapis ke luar — ke resource eksternal yang berada di luar proses Python (file, koneksi database, socket jaringan, lock) — dan melihat statement with dan context manager yang menangani akuisisi dan pelepasan mereka dengan aman.

Kenapa Kamu Perlu Statement with

Operasi seperti membuka file atau menghubung ke database datang dengan tugas cleanup: "begitu selesai, tutup". Lupa menutup, dan kamu membocorkan file descriptor, memegang koneksi DB selamanya, dan meninggalkan proses eksternal masih menggenggam resource.

Lupa menutup juga memegang resource proses eksternal
ProsesPythonbukakoneksiMySQL / OSpegang resourceclose()melepaskan keduanya ✅Python(sudah jalan)koneksimasih bukaMySQL / OSmasih pegangresourceFD habis /batas koneksitercapai ❌
File dan DB menyerahkan resource ke OS atau proses lain di luar Python. Kalau Python tidak memanggil close(), sisi lain terus menunggu instruksi dan memegang resource.

Kamu bisa menulis logika yang sama dengan try / finally, tetapi kemudian setiap penulis harus ingat memanggil close() di dalam finally setiap kali. Saat codebase tumbuh atau lebih banyak orang menyentuhnya, seseorang akan lupa — itu kenyataan.

Statement with menutup akuisisi dan pelepasan ke dalam satu unit sintaktis dan mengotomatiskannya. with open("file.txt") as f: adalah contoh kanonik: file dijamin tertutup begitu kamu meninggalkan blok with.

Alur eksekusi with X() as y:
with X() as y:masuk__enter__dipanggilnilai kembalimasuk ke ybodyblok__exit__cleanupreturnkeluar
Saat masuk, __enter__ berjalan dan nilai kembaliannya masuk ke variabel yang dinamai setelah as. Saat keluar — normal atau exceptional — __exit__ selalu dipanggil untuk cleanup.

Menulis Context Manager Sendiri — __enter__ dan __exit__

Object yang bisa dipakai dengan with disebut context manager. Untuk membuat class menjadi satu, cukup implementasikan dua special method.

- __enter__(self) — berjalan saat kamu memasuki blok with. Nilai kembaliannya diikat ke variabel yang dinamai setelah as.

- __exit__(self, exc_type, exc_val, traceback) — berjalan saat kamu meninggalkan blok. Selalu dipanggil, keluar normal atau exceptional.

Contoh gaya koneksi DB minimal di bawah (kita tidak benar-benar memakai library DB — kita meniru dengan string).

class DatabaseManager:
    def __init__(self, db_name):
        self.db_name    = db_name
        self.connection = None       # belum terhubung

    def __enter__(self):
        print(f"Connecting to {self.db_name}")
        self.connection = f"connection_to_{self.db_name}"   # kode nyata: object koneksi sebenarnya
        return self.connection                              # nilai diikat ke variabel as

    def __exit__(self, exc_type, exc_val, traceback):
        print(f"Disconnecting from {self.db_name}")
        self.connection = None                              # cleanup
        return False                                        # jangan menelan exception


with DatabaseManager("user_data_db") as conn:
    print(f"  active connection: {conn}")
    print("  inserting data")
# ↑ saat blok ini keluar, __exit__ berjalan

Jalankan dan output muncul dalam urutan "hubung → kerja dalam blok → putus". Pemutusan berjalan tanpa siapa pun memanggilnya secara eksplisit — itulah seluruh nilai with. Developer terbebas dari khawatir menutup koneksi.

Tulis DatabaseManager yang sama dari sampel sendiri dan jalankan dengan with.

① Definisikan class DatabaseManager: dan tetapkan self.db_name = db_name dan self.connection = None di __init__(self, db_name).

② Definisikan __enter__(self). Cetak f"Connecting to {self.db_name}", set self.connection = f"connection_to_{self.db_name}", lalu return self.connection.

③ Definisikan __exit__(self, exc_type, exc_val, traceback). Cetak f"Disconnecting from {self.db_name}", set self.connection = None, dan akhirnya return False.

④ Di dalam with DatabaseManager("user_data_db") as conn:, cetak f"active connection: {conn}" dan kemudian print("inserting data").

(Kalau kode kamu berjalan dengan benar, penjelasan akan muncul.)

Python Editor

Jalankan kode untuk melihat output

Tiga Argumen __exit__ — Menangkap Exception

__exit__ mengambil tiga argumen: exc_type, exc_val, traceback. Python memakai ini untuk memberitahu __exit__ apakah exception terjadi di dalam blok with.

- Keluar normal — ketiganya None. Cukup cleanup.

- Keluar exceptionexc_type adalah class exception, exc_val adalah instance, traceback adalah object traceback.

Nilai kembalian __exit__ juga punya makna. Mengembalikan True menelan exception — itu tidak merambat di luar blok. Mengembalikan False / None mengangkat ulang setelah cleanup. Default seharusnya False (atau return tanpa apa-apa): log atau notifikasi, tetapi selalu biarkan exception lolos.

Nilai konkret yang disampaikan ke 3 argumen __exit__
apa yang terjadidi withexc_typeexc_valtracebackkeluar normal(tanpa exception)NoneNoneNoneraiseValueError("invalid")<class'ValueError'>ValueError('invalid')<tracebackobject>
Keluar normal mengirim None × 3. Exception mengirim trio "class / instance / traceback". Sebuah raise ValueError("invalid") konkret membuat isinya nyata.
Keluar normal vs keluar exception di __exit__
keluar normaldari withexc_type =None, dll.__exit__cleanup sajakeluar dariblok withexceptiondi dalam withexc_type / val/ traceback__exit__log + cleanupreturn False-> merambat
Saat blok memunculkan exception, informasi exception dibungkus ke dalam tiga argumen __exit__. Nilai kembali memilih menelan (True) atau mengangkat ulang (False).

Mengembalikan True dari __exit__ diam-diam membunuh exception

Kalau __exit__ mengembalikan True, exception di dalam with tidak merambat. Menggoda, tetapi pemanggil sekarang berpikir operasi berhasil — itu efek samping serius. Default ke False (atau tanpa return): log atau notifikasi kalau mau, tetapi selalu biarkan exception muncul ke atas.

Sekarang benar-benar picu exception di dalam with dan lihat apa yang mencapai tiga argumen __exit__.

Modifikasi DatabaseManager Latihan 1 untuk mengonfirmasi dua hal: __exit__ berjalan bahkan saat exception, dan tiga argumennya menerima nilai.

① Definisikan class DatabaseManager: dan tetapkan self.db_name = db_name di __init__(self, db_name).

② Di __enter__(self), cetak f"Enter: {self.db_name}" dan return self (agar as mengikat manager itu sendiri).

③ Di __exit__(self, exc_type, exc_val, traceback), cetak ketiga argumen baris demi baris (print("exc_type:", exc_type) dan seterusnya), lalu print(f"Exit: {self.db_name}"), dan akhirnya return False.

④ Di dalam blok try:, buka with DatabaseManager("shop_db"):, cetak "start", lalu raise ValueError("bad inventory data").

⑤ Tangkap dengan except ValueError as e: dan print(f"caught outside: {e}").

Python Editor

Jalankan kode untuk melihat output

Dibandingkan dengan try / finally — Kenapa with Menang

Tugas context manager bisa dilakukan dengan try / finally. Alasan memilih with sebagai gantinya adalah "pasangan buka/tutup tinggal di dalam class". Menulis tugas yang sama dengan dua cara membuat perbedaan dalam volume kode pemanggil dan kejernihan menjadi jelas.

# ❌ try / finally — pemanggil menulis cleanup setiap kali
db = DatabaseManager("shop_db")
conn = db.open()                      # method connect kustom
try:
    use(conn)                         # kerja sebenarnya
finally:
    db.close()                        # jangan lupa — disalin di mana-mana


# ✅ with — buka/tutup tinggal di class, pemanggil hanya melakukan kerja
with DatabaseManager("shop_db") as conn:
    use(conn)                         # tidak perlu finally
with memusatkan tanggung jawab buka/tutup di class
Kalau kamu pakai try / finally
  • Pemanggil — harus menulis try / finally setiap kali
  • Lupa — satu copy-paste buruk dan leak muncul
  • Biaya perubahan — langkah cleanup ekstra berarti mengedit setiap tempat panggil
statement with + context manager
  • Pemanggil — satu baris, with X() as y:
  • Lupa — mustahil di tingkat sintaks (__exit__ selalu berjalan)
  • Biaya perubahan — cleanup ekstra berarti mengedit hanya __exit__
Memisahkan "pengguna resource" dari "pemilik buka/tutup" adalah nilai yang ditambah with. Saat tempat panggil berlipat ganda, perubahan cleanup tetap tinggal di dalam satu class.

Pakai with di mana pun akuisisi dan pelepasan berpasangan

File, koneksi DB, lock, socket jaringan — di mana pun kamu "meraih resource di awal dan harus mengembalikannya di akhir" — adalah kandidat untuk with. Library standar Python sudah mengekspos banyak dari ini sebagai context manager: open(), threading.Lock(), sqlite3.connect(), dan seterusnya.

QUIZ

Cek Pemahaman

Jawab setiap pertanyaan satu per satu.

Soal 1Di with X() as y:, nilai yang diikat ke y adalah nilai kembalian dari method mana?

Soal 2Saat exception dimunculkan di dalam blok with, mana yang menggambarkan perilaku __exit__ dengan benar?

Soal 3Kalau __exit__ mengembalikan True, apa yang terjadi pada exception yang dimunculkan di dalam blok with?