Menutup berkas secara manual dengan close() tampak mudah dalam contoh pendek, tetapi rapuh dalam program nyata. Begitu exception, return, atau break memutus alur di antara open() dan close(), pemanggilan close() terlewati dan berkas tetap menggantung terbuka — lengkap dengan data yang belum ter-flush dan file descriptor yang bocor. Statement with hadir untuk menghilangkan kelas bug ini sekaligus, dan kebetulannya pola yang sama juga dipakai banyak resource lain di luar berkas.

Bentuk Dasar with open(…) #

Statement with membuka berkas, menjalankan blok, lalu menutup berkas otomatis tepat saat blok berakhir:

python
with open("pidato.txt", "w", encoding="utf-8") as berkas:
    berkas.write("Baris pertama ditulis di dalam blok with.\n")
    print("Di dalam blok, berkas.closed =", berkas.closed)

print("Di luar blok, berkas.closed =", berkas.closed)
print(open("pidato.txt", encoding="utf-8").read(), end="")

Output yang Dihasilkan:

text
Di dalam blok, berkas.closed = False
Di luar blok, berkas.closed = True
Baris pertama ditulis di dalam blok with.

Mari kita amati mekanismenya:

  1. Baris 1 (with open(...) as berkas:): fungsi open() membuka berkas, lalu object file diikat ke variabel berkas hanya untuk durasi blok.
  2. Baris 2-3: method write() bekerja normal; atribut closed masih False karena kita masih di dalam blok.
  3. Baris 5-6: begitu blok berakhir, interpreter Python menutup berkas tanpa kita minta — atribut closed bernilai True, dan pembacaan ulang membuktikan datanya sudah masuk ke disk.

Di balik layar, object file mengimplementasikan context manager protocol: method __enter__() dipanggil saat blok dimulai, dan method __exit__() dipanggil saat blok berakhir dalam kondisi apa pun. Membuat context manager sendiri membutuhkan materi class pada section Object-Oriented Programming. Untuk sekarang, cukup tahu bahwa with tidak eksklusif milik berkas — object seperti TemporaryDirectory() pada Chapter Temporary Files dan berbagai kunci sinkronisasi juga dibuka dengan pola yang sama persis.

Penutupan Terjamin Bahkan Saat Exception #

Klaim “with selalu menutup berkas” mudah dibuktikan. Program berikut sengaja memicu ValueError di tengah blok with:

python
try:
    with open("hasil.txt", "w", encoding="utf-8") as berkas:
        berkas.write("Bagian ini sudah masuk ke disk.\n")
        angka = int("bukan angka")
except ValueError:
    print("ValueError tertangkap")

print("berkas.closed =", berkas.closed)
print(open("hasil.txt", encoding="utf-8").read(), end="")

Output yang Dihasilkan:

text
ValueError tertangkap
berkas.closed = True
Bagian ini sudah masuk ke disk.

Urutan kejadiannya begini:

  1. Baris 2 (with open(...)): berkas hasil.txt dibuka dengan mode tulis.
  2. Baris 3 (berkas.write(...)): satu baris masuk ke buffer lalu ke disk.
  3. Baris 4 (angka = int("bukan angka")): konversi gagal dan ValueError terlempar — blok with berakhir secara tidak normal.
  4. Sebelum exception diteruskan ke except, interpreter memanggil __exit__() milik object file, yang menutup berkas dan meng-flush data.
  5. Baris 8-9: penangan exception berjalan, dan dua baris terakhir membuktikan berkas.closed bernilai True serta data sebelum exception selamat.

Bandingkan dengan pola manual: tanpa with, exception pada baris keempat membuat close() tidak pernah terpanggil, data berpotensi hilang, dan file descriptor bocor. Penanganan exception sendiri telah dibahas pada Chapter try dan except.

Membuka Beberapa Berkas Sekaligus #

Kebutuhan membaca satu berkas sambil menulis berkas lain sangat umum, misalnya menyalin isi dengan transformasi ringan. Bentuk pertama adalah with bersarang — blok berikut menyiapkan sumber.txt terlebih dahulu, lalu menyalinnya baris demi baris:

python
with open("sumber.txt", "w", encoding="utf-8") as berkas:
    berkas.write("Budi Santoso\nSiti Nurhaliza\nJoko Anwar\n")

with open("sumber.txt", encoding="utf-8") as sumber:
    with open("salinan.txt", "w", encoding="utf-8") as tujuan:
        for baris in sumber:
            tujuan.write(baris)

print(open("salinan.txt", encoding="utf-8").read(), end="")

Output yang Dihasilkan:

text
Budi Santoso
Siti Nurhaliza
Joko Anwar

Indentasi bertingkat seperti ini cepat melebar. Python mengizinkan beberapa context manager dalam satu baris dengan tanda koma — blok berikut memakai sumber.txt yang barusan dibuat — dan bentuk inilah yang menjadi standar:

python
with open("sumber.txt", encoding="utf-8") as sumber, open("salinan.txt", "w", encoding="utf-8") as tujuan:
    for baris in sumber:
        tujuan.write(baris)

print(open("salinan.txt", encoding="utf-8").read(), end="")

Output yang Dihasilkan:

text
Budi Santoso
Siti Nurhaliza
Joko Anwar