# Exception di Python: try, except, else, finally, raise dan exception buatan sendiri

URL: https://skillaria.top/id/guides/python/exception-python

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 "", line 1, in
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
    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 `try` sependek 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 daripada `try/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.

## FAQ

**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.


---
Source: Skillaria.Top (https://skillaria.top)
