Peringatan lalu lintas punya dua rambu berbeda: satu untuk kondisi normal, satu untuk kondisi darurat. Pernyataan try di Python punya pembagian serupa melalui dua klausa opsional: else untuk jalur sukses, dan finally untuk pekerjaan yang wajib berjalan apa pun yang terjadi. Pada Chapter ini kita akan membedah kapan kedua klausa dieksekusi, termasuk kasus rumit seperti return dan break di tengah blok.

else: Kode yang Hanya Jalan Saat Sukses #

Klausa else hanya dieksekusi bila blok try selesai tanpa membangkitkan exception apa pun. Manfaat utamanya bukan sekadar gaya penulisan, melainkan menjaga blok try tetap ramping: kode yang tidak berisiko gagal dipindah ke else, sehingga exception dari kode tersebut tidak ikut tertangkap secara tak sengaja.

python
def ke_huruf_kecil(teks):
    try:
        hasil = teks.lower()
    except AttributeError:
        print("Input harus berupa string")
        return None
    else:
        print("Konversi berhasil")
        return hasil

print(ke_huruf_kecil("Kopi Gayo"))
print(ke_huruf_kecil(42))

Output yang Dihasilkan:

text
Konversi berhasil
kopi gayo
Input harus berupa string
None

Mari kita telaah apa yang terjadi pada baris-baris kode di atas:

  1. Baris 3 (hasil = teks.lower()): pada pemanggilan "Kopi Gayo", method lower() sukses sehingga klausa except dilewati dan klausa else dieksekusi.
  2. Baris 4 (except AttributeError:): pada pemanggilan 42, integer tidak punya method lower(), sehingga AttributeError dibangkitkan dan handler-nya berjalan.
  3. Baris 8–9 (isi blok else): pesan sukses dan hasil konversi hanya muncul pada pemanggilan pertama — bukti bahwa else benar-benar dijalankan hanya saat blok try bersih.

Pertanyaan yang wajar: bukankah baris 8–9 bisa saja ditaruh di akhir blok try? Bisa, tetapi ada harga yang dibayar. print("Konversi berhasil") di dalam try tidak berisiko gagal — bila ditaruh di sana, exception tak terduga dari baris tersebut (misalnya salah ketik nama method) akan ikut tertangkap klausa except AttributeError: di atasnya, mengaburkan sumber masalah. Klausa else memisahkan “kode yang diawasi” dari “kode yang hanya jalan saat aman”, sehingga jangkauan penangkapan tetap presisi.

finally: Pembersihan yang Selalu Berjalan #

Klausa finally dijalankan dalam semua skenario: sukses, exception tertangkap, maupun exception yang tidak tertangkap sekalipun. Inilah tempat yang tepat untuk kode pembersihan seperti menutup koneksi atau mengembalikan status:

python
def proses_pesanan(nomor, jumlah):
    try:
        print(f"Memproses pesanan #{nomor}")
        hasil = 1000 // jumlah
        print("Hasil:", hasil)
    except ZeroDivisionError:
        print("Jumlah pesanan tidak boleh nol")
    finally:
        print(f"Menutup pesanan #{nomor}")

proses_pesanan(101, 4)
print("---")
proses_pesanan(102, 0)

Output yang Dihasilkan:

text
Memproses pesanan #101
Hasil: 250
Menutup pesanan #101
---
Memproses pesanan #102
Jumlah pesanan tidak boleh nol
Menutup pesanan #102

Pada pemanggilan pertama, finally berjalan setelah blok try selesai. Pada pemanggilan kedua, ZeroDivisionError muncul di tengah blok try, handler except berjalan, lalu blok finally tetap berjalan untuk menutup pesanan tersebut. Satu skenario penting yang tak terlihat di output: bila exception tidak cocok dengan klausa except mana pun, blok finally tetap dieksekusi terlebih dahulu, baru kemudian exception diteruskan ke pemanggil. Jaminan inilah yang membuat finally menjadi tempat pembersihan yang aman — kode pembersihan tidak pernah terlewati, apa pun akhir ceritanya. Tabel berikut merangkum kombinasi lengkapnya:

Skenariotryexceptelsefinally
Tanpa exceptionselesai penuhdilewatijalanjalan
Exception cocok dengan exceptberhenti di titik errorjalandilewatijalan
Exception tidak cocokberhenti di titik errordilewatidilewatijalan, lalu exception diteruskan
Exception baru di dalam exceptselesai penuherror di dalamnyadilewatijalan, lalu error diteruskan

Alur lengkap pengambilan keputusan tersebut dapat kita lihat pada diagram berikut:

finally versus return dan break #

Klausa finally tetap dijalankan meskipun blok try berisi return. Eksekusinya bahkan mendahului pengiriman nilai balik:

python
def hitung_diskon(harga, persen):
    try:
        return harga * persen // 100
    finally:
        print("Blok finally dieksekusi sebelum nilai balik terkirim")

print(hitung_diskon(250000, 20))

Output yang Dihasilkan:

text
Blok finally dieksekusi sebelum nilai balik terkirim
50000

Perhatikan urutannya: pesan dari finally tercetak lebih dulu, baru kemudian 50000 — nilai yang dikembalikan function hitung_diskon(). Pernyataan break pun tidak bisa lolos dari finally:

python
for jumlah in [10, 0, 30]:
    try:
        print("Hasil:", 100 // jumlah)
        break
    finally:
        print("Blok finally tetap berjalan walau ada break")

Output yang Dihasilkan:

text
Hasil: 10
Blok finally tetap berjalan walau ada break

Perulangan berhenti setelah iterasi pertama karena break, tetapi interpreter Python mengeksekusi finally lebih dulu sebelum benar-benar keluar dari loop.

python
def berisiko():
    try:
        raise ValueError("error asli hilang tanpa jejak")
    finally:
        return "nilai dari blok finally"

print(berisiko())

Output yang Dihasilkan:

text
nilai dari blok finally

ValueError pada blok try seharusnya menghentikan program, tetapi return di dalam finally menelan error tersebut dan menggantinya dengan string. Program berlanjut seolah tidak pernah ada error.

Untuk kebutuhan sehari-hari seperti membaca file, Python menyediakan pola pembersihan yang lebih ringkas bernama context manager (with) — kita akan membahasnya pada Chapter Context Manager dan with. Pola try-finally tetap relevan ketika pembersihan membutuhkan logika khusus yang tidak tersedia dalam context manager.