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.
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:
Konversi berhasil
kopi gayo
Input harus berupa string
NoneMari kita telaah apa yang terjadi pada baris-baris kode di atas:
- Baris 3 (
hasil = teks.lower()): pada pemanggilan"Kopi Gayo", methodlower()sukses sehingga klausaexceptdilewati dan klausaelsedieksekusi. - Baris 4 (
except AttributeError:): pada pemanggilan42, integer tidak punya methodlower(), sehinggaAttributeErrordibangkitkan dan handler-nya berjalan. - Baris 8–9 (isi blok
else): pesan sukses dan hasil konversi hanya muncul pada pemanggilan pertama — bukti bahwaelsebenar-benar dijalankan hanya saat bloktrybersih.
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:
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:
Memproses pesanan #101
Hasil: 250
Menutup pesanan #101
---
Memproses pesanan #102
Jumlah pesanan tidak boleh nol
Menutup pesanan #102Pada 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:
| Skenario | try | except | else | finally |
|---|---|---|---|---|
| Tanpa exception | selesai penuh | dilewati | jalan | jalan |
| Exception cocok dengan except | berhenti di titik error | jalan | dilewati | jalan |
| Exception tidak cocok | berhenti di titik error | dilewati | dilewati | jalan, lalu exception diteruskan |
| Exception baru di dalam except | selesai penuh | error di dalamnya | dilewati | jalan, 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:
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:
Blok finally dieksekusi sebelum nilai balik terkirim
50000Perhatikan urutannya: pesan dari finally tercetak lebih dulu, baru kemudian 50000 — nilai yang dikembalikan function hitung_diskon(). Pernyataan break pun tidak bisa lolos dari finally:
for jumlah in [10, 0, 30]:
try:
print("Hasil:", 100 // jumlah)
break
finally:
print("Blok finally tetap berjalan walau ada break")Output yang Dihasilkan:
Hasil: 10
Blok finally tetap berjalan walau ada breakPerulangan berhenti setelah iterasi pertama karena break, tetapi interpreter Python mengeksekusi finally lebih dulu sebelum benar-benar keluar dari loop.
def berisiko():
try:
raise ValueError("error asli hilang tanpa jejak")
finally:
return "nilai dari blok finally"
print(berisiko())Output yang Dihasilkan:
nilai dari blok finallyValueError 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.