Exception di Python: try, except, else, finally, raise dan exception buatan sendiri
Exception di Python adalah objek error yang menghentikan jalannya kode sampai ditangkap oleh blok try/except; else dijalankan jika tidak ada error, dan finally selalu dijalankan. Error bisa dimunculkan sendiri dengan instruksi raise, misalnya raise ValueError("jumlah negatif"), dan exception buatan sendiri dibuat dengan kelas turunan Exception.
Exception di Python adalah objek yang menandai terjadinya error dan menghentikan jalannya program biasa. Jika tidak ada yang menangkapnya, Python mencetak traceback dan program berhenti. Exception ditangkap dengan konstruksi try/except, dan dimunculkan dengan instruksi raise. Berikut dibahas cara kerjanya, error yang paling sering muncul, dan cara menangani error tanpa menyembunyikannya.
Apa itu exception di Python
Ketika suatu operasi tidak bisa dijalankan — pembagian dengan nol, mengakses kunci yang tidak ada, mengubah kata menjadi angka — Python membuat objek exception dan «melemparkannya». Eksekusi fungsi saat itu berhenti, exception naik melalui rantai pemanggilan sampai menemukan penangan yang cocok. Jika tidak ada, program berhenti.
>>> 10 / 0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
Jawaban untuk pertanyaan yang sering muncul: pembagian dengan nol di Python memunculkan exception ZeroDivisionError. Ini berlaku untuk /, //, dan %.
Cara membaca traceback
Traceback adalah laporan tentang bagaimana program sampai pada error. Membacanya lebih mudah dari bawah ke atas. Baris terakhir adalah yang terpenting: jenis exception dan pesannya. Di atasnya ada baris kode tempat kejadian, dan di atas itu lagi siapa yang memanggilnya, sampai titik masuk program.
Traceback (most recent call last):
File "tracker.py", line 9, in <module>
total = parse_amount(row)
File "tracker.py", line 4, in parse_amount
return float(raw.strip())
~~~~~^^^^^^^^^^^^^
ValueError: could not convert string to float: 'seratus'
Sejak Python 3.11, di bawah baris kode digambar tanda ~~~^^^ (PEP 657): tanda ini menunjukkan bagian mana dari ekspresi yang gagal. Ini sangat membantu ketika beberapa pemanggilan ditulis berurutan dalam satu baris.
Saran «Did you mean»
Sejak Python 3.10, jika ada salah ketik pada nama, error menawarkan nama yang mirip. Di Python 3.15 saran ini menjadi lebih pintar: interpreter mengenali kebiasaan dari bahasa lain. Pemanggilan list.push akan menyarankan append, dan "kopi".toUpperCase() akan menyarankan metode upper. Saran juga memeriksa field bersarang: jika dict ada di atribut totals dari sebuah objek, lalu Anda memanggil items() langsung pada objek itu, saran akan menyebut .totals.items. Nama-nama yang mirip juga kini disarankan oleh delattr().
Jenis exception di Python
Ada puluhan exception bawaan, tetapi di awal belajar Anda paling sering menemui yang berikut:
| Exception | Kapan muncul | Contoh |
|---|---|---|
NameError | Nama tidak didefinisikan, biasanya salah ketik | print(totl) |
TypeError | Operasi tidak cocok untuk tipe ini | "5" + 3 |
ValueError | Tipenya benar, nilainya tidak | int("seratus") |
KeyError | Kunci tidak ada di dict | d["missing"] |
IndexError | Indeks di luar list | [1, 2][5] |
AttributeError | Objek tidak punya metode atau field tersebut | [].push(1) |
ZeroDivisionError | Pembagian dengan nol | 1 / 0 |
FileNotFoundError | Berkas tidak ditemukan | open("nope.txt") |
Ada juga SyntaxError (dan kasus khususnya IndentationError): ini muncul saat berkas diurai, sebelum kode dijalankan, sehingga tidak bisa ditangkap dengan try di berkas yang sama.
Hierarki exception
Exception adalah kelas, dan kelas-kelas itu tersusun dalam hierarki. Di puncaknya ada BaseException. Dari situ turun Exception (hampir semua error «biasa») dan beberapa exception sistem: KeyboardInterrupt, SystemExit, GeneratorExit.
BaseException
├── KeyboardInterrupt
├── SystemExit
└── Exception
├── ArithmeticError
│ └── ZeroDivisionError
├── LookupError
│ ├── KeyError
│ └── IndexError
├── OSError
│ └── FileNotFoundError
├── TypeError
└── ValueError
Hierarki ini penting saat menangkap: except LookupError juga menangkap KeyError dan IndexError. Sementara KeyboardInterrupt bukan error dalam kode: ia menandakan bahwa pengguna menekan Ctrl+C. Ia sengaja tidak diturunkan dari Exception, supaya except Exception tidak menghalangi penghentian program.
Penanganan exception: try/except/else/finally
Kode yang mungkin gagal ditaruh di blok try. Di blok except ditulis jenis error dan apa yang harus dilakukan. except adalah blok yang dipakai Python untuk menangani exception.
def parse_amount(raw):
try:
value = float(raw)
except ValueError as e:
print(f"Melewati baris: {e}")
return None
else:
return value # dijalankan jika tidak ada error
finally:
print("parsing selesai") # selalu dijalankan
print(parse_amount("250"))
Urutan keluarannya: pertama «parsing selesai», lalu 250.0. return di blok else menyimpan nilainya, lalu Python pasti menjalankan finally dan baru setelah itu mengembalikan hasil. Karena itu finally bisa diandalkan untuk bersih-bersih: menutup berkas, melepas koneksi.
Jika di dalam finally ditulis return sendiri, ia akan menimpa yang sebelumnya dan bahkan «menelan» exception. Sejak Python 3.14, kode seperti ini menghasilkan SyntaxWarning (PEP 765) — jangan menulis return di dalam finally.
Beberapa exception sekaligus
Anda bisa menulis beberapa blok except untuk jenis yang berbeda, atau menyebutkan beberapa jenis dalam satu tuple:
try:
amount = int(data["amount"])
except KeyError:
amount = 0
except (ValueError, TypeError) as e:
print("Jumlah tidak valid:", e)
amount = 0
Blok diperiksa dari atas ke bawah, dan yang pertama cocok akan dijalankan, sehingga jenis yang lebih spesifik diletakkan di atas jenis yang lebih umum. Sejak Python 3.14 (PEP 758), beberapa jenis bisa ditulis tanpa tanda kurung — except ValueError, TypeError: — tetapi jika ada as, tanda kurung tetap wajib.
Cara menampilkan teks error dan mengetahui jenisnya
Objek exception tersedia lewat as e. str(e) memberikan pesannya, type(e).__name__ memberikan nama jenisnya:
try:
int("seratus")
except ValueError as e:
print(type(e).__name__) # ValueError
print(e) # invalid literal for int() with base 10: 'seratus'
Modul traceback: teks error lengkap
Kadang error perlu ditangkap, tetapi traceback lengkapnya tetap harus disimpan — untuk log atau laporan. Untuk itu ada modul traceback:
import traceback
try:
1 / 0
except ZeroDivisionError:
traceback.print_exc() # mencetak traceback ke stderr
text = traceback.format_exc() # teks yang sama dalam bentuk string
Jika Anda menulis log dengan modul logging, lebih mudah memanggil logging.exception("gagal menghitung") di dalam except — traceback otomatis ikut dicatat.
raise di Python: memunculkan exception
Instruksi raise melemparkan exception secara manual. Dengan begitu fungsi menyatakan bahwa ia tidak bisa mengerjakan tugasnya dengan data tersebut:
def add_expense(amount):
if amount < 0:
raise ValueError(f"Jumlah tidak boleh negatif: {amount}")
...
add_expense(-5)
# ValueError: Jumlah tidak boleh negatif: -5
raise di Python adalah instruksi, bukan fungsi, jadi tidak ada tanda kurung setelah kata raise: argumen dalam kurung diberikan kepada konstruktor exception. Pilih jenisnya sesuai maknanya: nilai yang salah — ValueError, tipe yang salah — TypeError.
raise tanpa argumen dan raise from
Di dalam except, raise tanpa argumen meneruskan exception yang sedang ditangani tanpa perubahan. Ini berguna ketika Anda ingin mencatat sesuatu ke log, tetapi penanganannya diserahkan ke kode pemanggil.
Jika Anda mengganti satu exception dengan exception lain, gunakan raise ... from e — error asli tetap tersimpan dalam rantai, dan traceback memperlihatkan keduanya:
def load_config(path):
try:
with open(path) as f:
return f.read()
except FileNotFoundError as e:
raise RuntimeError(f"Konfigurasi {path} tidak ditemukan") from e
add_note: menambahkan konteks pada error
Sejak Python 3.11 (PEP 678), exception bisa diberi catatan dengan metode add_note() lalu diteruskan lagi. Jenis error tidak berubah, dan catatan dicetak di bawah pesannya:
def parse(raw, line_no):
try:
return float(raw)
except ValueError as e:
e.add_note(f"berkas expenses.csv, baris {line_no}")
raise
parse("seratus", 17)
# ValueError: could not convert string to float: 'seratus'
# berkas expenses.csv, baris 17
Cara membuat exception sendiri di Python
Exception buatan sendiri diperlukan untuk error dari domain aplikasi: anggaran terlampaui, pesanan sudah dibayar, pengguna diblokir. Cukup buat kelas turunan Exception:
class BudgetExceeded(Exception):
def __init__(self, category, limit, spent):
super().__init__(f"{category}: batas {limit}, sudah dipakai {spent}")
self.category = category
self.limit = limit
self.spent = spent
def check_budget(category, limit, spent):
if spent > limit:
raise BudgetExceeded(category, limit, spent)
try:
check_budget("Makan", 10000, 12500)
except BudgetExceeded as e:
print("Peringatan:", e) # Peringatan: Makan: batas 10000, sudah dipakai 12500
Dengan begitu kode pemanggil menangkap tepat kasus anggaran terlampaui dan tidak mencampurnya dengan salah ketik. Dalam proyek besar, berguna untuk membuat kelas dasar bersama (class TrackerError(Exception)) dan menurunkan semua exception sendiri darinya — sehingga semuanya bisa ditangkap dengan satu except TrackerError. Turunkan dari Exception, bukan dari BaseException.
Grup exception dan except*
Kadang error terjadi bersamaan, misalnya saat memeriksa seluruh berkas atau pada tugas paralel di asyncio.TaskGroup. Sejak Python 3.11, error-error itu bisa dikumpulkan dalam ExceptionGroup, dan ditangani per jenis dengan except*:
def check_all(rows):
errors = []
for r in rows:
try:
float(r)
except ValueError as e:
errors.append(e)
if errors:
raise ExceptionGroup("error dalam berkas", errors)
try:
check_all(["1", "a", "b"])
except* ValueError as eg:
print(f"Baris rusak: {len(eg.exceptions)}") # Baris rusak: 2
Python try except semua error: mengapa itu tidak dianjurkan
Kebiasaan paling berbahaya adalah menangkap semuanya: except: pass. Program «tidak lagi jatuh», tetapi sebenarnya tidak lagi melaporkan masalah. except tanpa jenis menangkap bahkan KeyboardInterrupt dan SystemExit, sehingga program tidak bisa dihentikan dengan Ctrl+C. except Exception: pass sedikit lebih baik, tetapi tetap menelan diam-diam salah ketik nama dan kesalahan logika.
Cara menangani exception di Python dengan benar:
- tangkap hanya jenis yang bisa Anda tangani, misalnya
except ValueError, bukan semuanya; - buat blok
trysependek mungkin — hanya baris yang memang bisa gagal dengan cara itu; - jika menangkap secara luas (
except Exception) di level atas program, pastikan traceback dicatat ke log; - jangan memakai exception untuk percabangan biasa jika pengecekan sudah cukup: di kasus sederhana,
dict.get()lebih baik daripadatry/except KeyError; - untuk sumber daya, gunakan
with— ia akan menutup berkas bahkan ketika terjadi error.
Ringkasan
Exception di Python adalah mekanisme yang wajar untuk melaporkan error, bukan alasan untuk menyembunyikannya. Baca traceback dari bawah ke atas, tangkap jenis yang spesifik dalam try/except, gunakan else untuk kode «jika semuanya berhasil», dan finally untuk bersih-bersih. raise dipakai untuk melaporkan error sendiri, raise from dan add_note menjaga konteks, dan exception buatan sendiri diturunkan dari Exception. Topik ini dipraktikkan dalam kursus Python: pencatat pengeluaran tidak lagi jatuh karena satu baris CSV yang rusak, dan kelebihan anggaran dinyatakan dengan exception tersendiri.
Pertanyaan umum
Apa bedanya error dan exception di Python?
Dalam percakapan sehari-hari keduanya dianggap sama. Secara teknis, SyntaxError terdeteksi saat kode diurai, sedangkan error lain muncul saat program berjalan sebagai objek exception yang bisa ditangkap.
Bisakah eksekusi dilanjutkan dari titik error setelah exception ditangkap?
Tidak. Setelah ditangkap, eksekusi dilanjutkan setelah blok try/except, bukan dari baris tempat error terjadi. Jika setiap baris berkas harus diproses, letakkan try di dalam perulangan.
Apa yang terjadi jika exception muncul di dalam except?
Exception baru itu naik ke atas, sedangkan exception asli tersimpan di atribut __context__. Traceback akan memuat kalimat During handling of the above exception, another exception occurred.
Bagaimana menguji bahwa fungsi melempar exception?
Di pytest dipakai with pytest.raises(ValueError):, di unittest dipakai self.assertRaises(ValueError). Tes hanya lulus jika di dalam blok terjadi exception dengan jenis yang diharapkan.
Apa itu assert dan bedanya dengan raise?
assert kondisi, "pesan" melempar AssertionError jika kondisi bernilai salah. Ini alat pemeriksaan saat pengembangan: jika program dijalankan dengan flag -O, assert dinonaktifkan, jadi input pengguna sebaiknya diperiksa dengan if dan raise.
Topik ini dibahas mendalam dengan praktik di kursus “Python 3.15: от новичка до профи”.
Ke kursus