Python & AI Tutorials Logo
Pemrograman Python

25. Menangani Exception dengan Elegan

Pada Bab 24, kita belajar cara membaca dan memahami exception saat terjadi. Sekarang kita akan belajar cara menangani exception dengan elegan, sehingga program kita bisa pulih dari error alih-alih crash. Ini penting untuk menulis program yang tangguh, ramah pengguna, dan dapat menghadapi situasi tak terduga.

Saat sebuah exception terjadi di Python, alur normal program langsung berhenti. Tapi bagaimana kalau kita bisa menangkap exception itu sebelum membuat program kita crash? Bagaimana kalau kita bisa merespons error tersebut, misalnya dengan meminta pengguna mencoba lagi, atau memakai nilai default, atau mencatat masalahnya dan melanjutkan? Itulah tepatnya yang memungkinkan kita lakukan dengan penanganan exception.

25.1) Menggunakan Blok try dan except

25.1.1) Struktur Dasar try dan except

Sebuah blok try-except adalah cara Python untuk mengatakan "coba lakukan ini, dan jika exception terjadi, lakukan ini sebagai gantinya." Struktur dasarnya terlihat seperti ini:

python
try:
    # Kode yang mungkin me-raise exception
    risky_operation()
except:
    # Kode yang berjalan jika ADA exception apa pun
    print("Something went wrong!")

Blok try berisi kode yang mungkin me-raise exception. Jika sebuah exception terjadi di mana pun di dalam blok try, Python langsung berhenti mengeksekusi blok try dan melompat ke blok except. Jika tidak ada exception yang terjadi, blok except dilewati sepenuhnya.

Mari lihat contoh konkret. Ingat dari Bab 24 bahwa mencoba mengonversi string yang tidak valid menjadi integer akan me-raise ValueError:

python
# Tanpa penanganan exception - program crash
user_input = "hello"
number = int(user_input)  # ValueError: invalid literal for int() with base 10: 'hello'
print("This line never executes")

Sekarang mari tangani exception ini dengan elegan:

python
# Dengan penanganan exception - program lanjut
user_input = "hello"
 
try:
    number = int(user_input)
    print(f"You entered: {number}")
except:
    print("That's not a valid number!")
    number = 0  # Gunakan nilai default
 
print(f"Using number: {number}")

Output:

That's not a valid number!
Using number: 0

Programnya tidak crash! Ketika int(user_input) me-raise ValueError, Python melompat ke blok except, mencetak pesan error kita, menetapkan nilai default, lalu melanjutkan dengan sisa program.

Berikut yang terjadi langkah demi langkah:

Tidak

Ya

Mulai blok try

Jalankan konversi int

Exception di-raise?

Lanjut di blok try

Lompat ke blok except

Lewati blok except

Jalankan kode except

Lanjut setelah try-except

Memahami "Lompatan" - Apa yang Sebenarnya Terjadi

Saat kita mengatakan Python "melompat" ke blok except, maksudnya adalah Python meninggalkan eksekusi berurutan yang normal. Ini adalah perubahan mendasar pada bagaimana program kamu mengalir—bukan sekadar percabangan sederhana seperti statement if. Mari lihat ini secara detail dengan contoh konkret:

python
# Mengamati alur eksekusi dengan exception
print("1. Starting program")
 
try:
    print("2. Entered try block")
    number = int("hello")  # Exception terjadi DI SINI
    print("3. After conversion")   # Baris ini TIDAK PERNAH dieksekusi
    result = number * 2            # Baris ini TIDAK PERNAH dieksekusi
    print("4. After calculation")  # Baris ini TIDAK PERNAH dieksekusi
except ValueError:
    print("5. In except block - handling the error")
 
print("6. After try-except block")

Output:

1. Starting program
2. Entered try block
5. In except block - handling the error
6. After try-except block

Perhatikan bahwa baris 3 dan 4 tidak pernah dieksekusi! Saat int("hello") me-raise ValueError, Python:

  1. Berhenti mengeksekusi blok try seketika—tepat di baris tempat exception terjadi
  2. Mencari klausa except yang cocok yang bisa menangani tipe exception ini
  3. Melompat ke blok except tersebut, melewati semua sisa kode di blok try
  4. Melanjutkan setelah struktur try-except begitu blok except selesai

Ini pada dasarnya berbeda dari alur program normal. Dalam eksekusi normal, Python menjalankan tiap baris secara berurutan. Dengan exception, Python meninggalkan jalur saat ini dan mengambil rute yang benar-benar berbeda. Tanpa penanganan exception, program akan crash pada baris 2 dan berhenti. Dengan penanganan exception, program pulih dan lanjut.

Mengapa Ini Penting:

Memahami perilaku "lompatan" ini sangat krusial karena:

  • Kode apa pun setelah exception di blok try akan dilewati — kamu tidak bisa mengasumsikan baris-baris berikutnya di blok try sempat dieksekusi
  • Variabel mungkin belum terinisialisasi jika exception terjadi sebelum assignment-nya
  • Kamu perlu merencanakan kondisi (state) program saat blok except berjalan

25.1.2) Menangani Input Pengguna dengan Aman

Salah satu penggunaan paling umum dari penanganan exception adalah memvalidasi input pengguna. Pengguna bisa mengetik apa saja, dan kita perlu menangani input yang tidak valid dengan elegan. Berikut contoh praktis program yang meminta umur pengguna:

python
# Input umur yang aman dengan penanganan exception
print("Please enter your age:")
user_input = input()
 
try:
    age = int(user_input)
    print(f"You are {age} years old.")
    
    # Hitung tahun lahir (asumsikan tahun saat ini 2024)
    birth_year = 2024 - age
    print(f"You were born around {birth_year}.")
except:
    print("Invalid input! Age must be a number.")
    print("Using default age of 0.")
    age = 0

Jika pengguna memasukkan "25", output-nya adalah:

Please enter your age:
25
You are 25 years old.
You were born around 1999.

Jika pengguna memasukkan "twenty-five", output-nya adalah:

Please enter your age:
twenty-five
Invalid input! Age must be a number.
Using default age of 0.

Perhatikan bagaimana program menangani error dengan elegan alih-alih crash dengan traceback. Ini jauh lebih baik untuk pengalaman pengguna.

25.1.3) Menangani Banyak Operasi dalam Blok try

Kamu bisa menaruh beberapa operasi dalam satu blok try. Jika salah satunya me-raise exception, Python langsung melompat ke blok except. Mari mulai dengan contoh sederhana:

python
# Dua operasi di blok try
print("Enter a number:")
user_input = input()
 
try:
    number = int(user_input)      # Operasi pertama - bisa me-raise ValueError
    result = 100 / number          # Operasi kedua - bisa me-raise ZeroDivisionError
    print(f"100 / {number} = {result}")
except:
    print("Something went wrong!")

Jika pengguna memasukkan "hello", exception terjadi pada operasi pertama (konversi). Jika pengguna memasukkan "0", exception terjadi pada operasi kedua (pembagian). Bagaimanapun, satu blok except kita menangkapnya.

Sekarang mari perluas ini menjadi tiga operasi:

python
# Banyak operasi di blok try
print("Enter two numbers to divide:")
numerator_input = input("Numerator: ")
denominator_input = input("Denominator: ")
 
try:
    numerator = int(numerator_input)      # Bisa me-raise ValueError
    denominator = int(denominator_input)  # Bisa me-raise ValueError
    result = numerator / denominator      # Bisa me-raise ZeroDivisionError
    print(f"{numerator} / {denominator} = {result}")
except:
    print("Something went wrong with the calculation!")
    print("Make sure you enter valid numbers and don't divide by zero.")

Jika pengguna memasukkan "10" dan "2":

Enter two numbers to divide:
Numerator: 10
Denominator: 2
10 / 2 = 5.0

Jika pengguna memasukkan "10" dan "zero":

Enter two numbers to divide:
Numerator: 10
Denominator: zero
Something went wrong with the calculation!
Make sure you enter valid numbers and don't divide by zero.

Jika pengguna memasukkan "10" dan "0":

Enter two numbers to divide:
Numerator: 10
Denominator: 0
Something went wrong with the calculation!
Make sure you enter valid numbers and don't divide by zero.

Dalam contoh ini, tiga hal berbeda bisa salah: konversi pembilang bisa gagal, konversi penyebut bisa gagal, atau pembagian bisa gagal (jika penyebut nol). Satu blok except kita menangkap semua kasus ini. Namun, pendekatan ini punya keterbatasan: kita tidak bisa tahu error spesifik mana yang terjadi. Kita akan membahas ini pada bagian berikutnya.

25.1.4) Masalah dengan Klausa except Kosong (Bare except)

Menggunakan except: tanpa menyebutkan tipe exception disebut klausa bare except. Meskipun menangkap semua exception, ini sering kali terlalu luas dan bisa menyembunyikan masalah tak terduga. Pertimbangkan contoh ini:

python
# Bare except menangkap SEMUANYA - bahkan hal yang tidak kita duga
numbers = [10, 20, 30]
 
try:
    index = 5  # Kita mengharapkan IndexError jika index di luar rentang
    value = numbers[index]
    print(f"Value at index {index}: {value}")
except:
    print("Could not access the list element.")

Ini terlihat masuk akal—kita mencoba mengakses elemen list yang mungkin tidak ada. Tapi bagaimana kalau ada typo di kode kita?

python
# Bagaimana jika ada typo di kode kita?
numbers = [10, 20, 30]
 
try:
    index = 2
    value = numbrs[index]  # Typo: 'numbrs' alih-alih 'numbers'
    print(f"Value at index {index}: {value}")
except:
    print("Could not access the list element.")

Output:

Could not access the list element.

Bare except menangkap NameError dari typo dan mencetak "Could not access the list element"—memberi kita informasi yang salah tentang apa yang keliru! Kita mengira index di luar rentang, padahal sebenarnya ada typo pada nama variabel.

Bare except juga menangkap KeyboardInterrupt (saat pengguna menekan Ctrl+C) dan SystemExit (saat program mencoba keluar), yang biasanya tidak seharusnya ditangkap. Karena alasan ini, lebih baik menangkap exception yang spesifik, yang akan kita pelajari berikutnya.

25.2) Menangkap Exception Spesifik

25.2.1) Menentukan Tipe Exception

Alih-alih menangkap semua exception dengan bare except, kita bisa menentukan tipe exception mana yang ingin kita tangani. Ini membuat kode kita lebih presisi dan membantu kita merespons secara tepat untuk error yang berbeda:

python
# Menangkap tipe exception spesifik
user_input = "hello"
 
try:
    number = int(user_input)
    print(f"You entered: {number}")
except ValueError:
    print("That's not a valid number!")
    number = 0
 
print(f"Using number: {number}")

Output:

That's not a valid number!
Using number: 0

Sekarang klausa except kita hanya menangkap exception ValueError. Jika tipe exception yang berbeda terjadi (seperti NameError dari typo), itu tidak akan tertangkap, dan kita akan melihat traceback lengkap—yang sebenarnya membantu untuk debugging!

Sintaksnya adalah: except ExceptionType: di mana ExceptionType adalah nama kelas exception yang ingin kamu tangkap (seperti ValueError, TypeError, ZeroDivisionError, dll.).

Kesalahan Umum: Menangkap Tipe Exception yang Salah

Apa yang terjadi jika kamu menentukan tipe exception yang tidak cocok dengan yang sebenarnya terjadi? Mari lihat:

python
# Menangkap tipe exception yang salah
user_input = "hello"
 
try:
    number = int(user_input)  # Ini me-raise ValueError
    print(f"You entered: {number}")
except TypeError:  # Tapi kita menangkap TypeError!
    print("That's not a valid number!")
    number = 0
 
print(f"Using number: {number}")

Output:

Traceback (most recent call last):
  File "example.py", line 4, in <module>
    number = int(user_input)
ValueError: invalid literal for int() with base 10: 'hello'

Programnya crash! Kenapa? Karena int("hello") me-raise ValueError, tetapi klausa except kita hanya menangkap TypeError. Karena tidak ada klausa except yang cocok, exception tidak tertangkap, dan program berhenti.

Ini sebenarnya membantu selama pengembangan—kalau kamu menangkap tipe exception yang salah, kamu akan melihat traceback lengkap dan menyadari kesalahanmu. Ini salah satu alasan mengapa menangkap exception spesifik lebih baik daripada menggunakan bare except.

Cara menghindari kesalahan ini:

  1. Baca traceback untuk melihat tipe exception apa yang benar-benar terjadi
  2. Gunakan tipe exception spesifik itu di klausa except kamu
  3. Jika kamu tidak yakin, jalankan kode dan biarkan crash—traceback akan memberi tahu kamu!

25.2.2) Menangani Exception Berbeda dengan Cara Berbeda

Kamu bisa punya beberapa klausa except untuk menangani tipe exception yang berbeda dengan cara yang berbeda. Ini sangat berguna ketika error yang berbeda membutuhkan respons yang berbeda:

python
# Penanganan berbeda untuk exception yang berbeda
print("Enter two numbers to divide:")
numerator_input = input("Numerator: ")
denominator_input = input("Denominator: ")
 
try:
    numerator = int(numerator_input)
    denominator = int(denominator_input)
    result = numerator / denominator
    print(f"{numerator} / {denominator} = {result}")
except ValueError:
    print("Error: Both inputs must be valid integers.")
    print("You entered something that isn't a number.")
except ZeroDivisionError:
    print("Error: Cannot divide by zero.")
    print("The denominator must be a non-zero number.")

Jika pengguna memasukkan "10" dan "abc":

Enter two numbers to divide:
Numerator: 10
Denominator: abc
Error: Both inputs must be valid integers.
You entered something that isn't a number.

Jika pengguna memasukkan "10" dan "0":

Enter two numbers to divide:
Numerator: 10
Denominator: 0
Error: Cannot divide by zero.
The denominator must be a non-zero number.

Python memeriksa setiap klausa except secara berurutan. Saat exception terjadi, Python menemukan klausa except pertama yang cocok dengan tipe exception tersebut dan mengeksekusi blok itu. Klausa except lainnya dilewati.

Ya

Tidak

Ya

Tidak

Ya

Tidak

Exception di-raise di blok try

Cocok dengan except pertama?

Jalankan blok except pertama

Cocok dengan except kedua?

Jalankan blok except kedua

Cocok dengan except ketiga?

Jalankan blok except ketiga

Exception tidak tertangkap - menerus (propagate) ke atas

Lanjut setelah try-except

25.2.3) Menangkap Banyak Tipe Exception dalam Satu Klausa

Terkadang kamu ingin menangani beberapa tipe exception yang berbeda dengan cara yang sama. Alih-alih menulis beberapa blok except yang identik, kamu bisa menangkap banyak tipe exception dalam satu klausa dengan menaruhnya dalam tanda kurung sebagai tuple:

python
# Menangkap beberapa tipe exception sekaligus
print("Enter a number:")
user_input = input()
 
try:
    number = int(user_input)
    result = 100 / number
    print(f"100 divided by {number} is {result}")
except (ValueError, ZeroDivisionError):
    print("Invalid input or division by zero.")
    print("Please enter a non-zero number.")

Jika pengguna memasukkan "hello":

Enter a number:
hello
Invalid input or division by zero.
Please enter a non-zero number.

Jika pengguna memasukkan "0":

Enter a number:
0
Invalid input or division by zero.
Please enter a non-zero number.

Baik ValueError (dari konversi yang tidak valid) maupun ZeroDivisionError (dari pembagian nol) ditangani oleh klausa except yang sama. Ini berguna ketika error yang berbeda seharusnya memicu respons yang sama.

25.2.4) Mengakses Informasi Exception

Terkadang kamu perlu tahu detail lebih lanjut tentang exception yang terjadi. Kamu bisa menangkap objek exception menggunakan keyword as. Tapi pertama, mari pahami apa itu objek exception.

Apa Itu Objek Exception?

Saat Python me-raise exception, Python tidak hanya memberi sinyal bahwa sesuatu salah—Python membuat sebuah objek yang berisi informasi tentang error. Objek exception ini seperti laporan error yang detail yang mencakup:

  • Pesan error: Deskripsi tentang apa yang salah
  • Tipe exception: Jenis error yang terjadi (ValueError, TypeError, dll.)
  • Atribut tambahan: Informasi spesifik tergantung tipe exception

Anggap objek exception sebagai wadah yang menampung semua informasi tentang sebuah error. Seperti objek list berisi item dan punya method seperti append(), objek exception berisi informasi error dan punya atribut yang bisa kamu akses.

Saat kamu menulis except ValueError as error:, kamu memberi tahu Python: "Jika ValueError terjadi, buat variabel bernama error dan masukkan objek exception ke dalamnya supaya aku bisa memeriksanya."

Mari eksplor isi sebuah objek exception:

python
# Memeriksa isi objek exception
try:
    number = int("hello")
except ValueError as error:
    print("Exception caught! Let's examine it:")
    print(f"Type: {type(error)}")
    print(f"String representation: {error}")
    print(f"Args tuple: {error.args}")

Output:

Exception caught! Let's examine it:
Type: <class 'ValueError'>
String representation: invalid literal for int() with base 10: 'hello'
Args tuple: ("invalid literal for int() with base 10: 'hello'",)

Objek exception memiliki:

  • Sebuah type (kelas ValueError) — ini memberitahumu jenis error yang terjadi
  • Sebuah representasi string (pesan error) — ini yang kamu lihat di traceback
  • Atribut args (tuple yang berisi pesan dan argumen lain) — ini memberikan akses terstruktur ke detail error

Mengapa Ini Penting:

Tipe exception yang berbeda punya atribut yang berbeda yang menyediakan informasi spesifik. Memahami struktur objek exception membantumu mengekstrak informasi berguna untuk debugging atau umpan balik ke pengguna:

python
# Exception yang berbeda punya atribut yang berbeda
numbers = [10, 20, 30]
 
try:
    value = numbers[10]
except IndexError as error:
    print(f"IndexError message: {error}")
    print(f"Exception args: {error.args}")
 
# Sekarang coba dengan dictionary
grades = {"Alice": 95}
 
try:
    grade = grades["Bob"]
except KeyError as error:
    print(f"KeyError message: {error}")
    print(f"Missing key: {error.args[0]}")

Output:

IndexError message: list index out of range
Exception args: ('list index out of range',)
KeyError message: 'Bob'
Missing key: Bob

Perhatikan bagaimana KeyError menyertakan key sebenarnya yang hilang dalam pesannya. Tipe exception yang berbeda menyediakan informasi berguna yang berbeda yang bisa kamu akses lewat objek exception.

25.3) Menggunakan else dan finally dengan Blok try

25.3.1) Klausa else: Kode yang Berjalan Hanya Saat Sukses

Klausa else dalam blok try-except berjalan hanya jika tidak ada exception yang terjadi di blok try. Ini berguna untuk kode yang hanya boleh dieksekusi ketika operasi berisiko berhasil:

python
# Menggunakan else untuk kode yang hanya berjalan saat sukses
print("Enter a number:")
user_input = input()
 
try:
    number = int(user_input)
except ValueError:
    print("That's not a valid number!")
else:
    # Ini hanya berjalan jika int(user_input) berhasil
    print(f"Successfully converted: {number}")
    squared = number ** 2
    print(f"The square of {number} is {squared}")

Jika pengguna memasukkan "5":

Enter a number:
5
Successfully converted: 5
The square of 5 is 25

Jika pengguna memasukkan "hello":

Enter a number:
hello
That's not a valid number!

Kenapa memakai else alih-alih menaruh kode itu di akhir blok try saja? Ada dua alasan penting:

  1. Kejelasan: Klausa else membuat jelas bahwa kode ini hanya berjalan saat sukses
  2. Cakupan exception: Exception yang di-raise di klausa else tidak ditangkap oleh klausa except sebelumnya

Berikut contoh yang menunjukkan kenapa poin kedua itu penting:

python
# Mendemonstrasikan mengapa else berguna untuk cakupan exception
try:
    number_1 = int(input("Enter a number_1: "))
except ValueError:
    print("Invalid input!")
else:
    # Jika error terjadi di sini, itu tidak akan tertangkap oleh except di atas
    # Ini membantu membedakan antara error input dan error pemrosesan
    number_2 = int(input("Enter a number_2: ")) # Bisa me-raise ValueError

Jika kita menaruh number_2 = int(input(...)) di blok try bersama number_1, ValueError apa pun dari salah satu input akan ditangkap oleh klausa except ValueError yang sama. Ini membuat kita tidak bisa membedakan input mana yang menyebabkan masalah.

Dengan menaruh number_2 = int(input(...)) di blok else, kita memisahkan penanganan error. Klausa except hanya menangkap error dari number_1, sementara error dari number_2 akan me-raise exception yang tidak tertangkap dengan traceback lengkap—membuat jelas bahwa input kedua yang gagal, bukan yang pertama.

25.3.2) Klausa finally: Kode yang Selalu Berjalan

Klausa finally berisi kode yang berjalan apa pun yang terjadi—baik exception terjadi atau tidak, baik tertangkap atau tidak. Ini penting untuk operasi cleanup yang harus selalu terjadi:

python
# Menggunakan finally untuk cleanup
print("Enter a number:")
user_input = input()
 
try:
    number = int(user_input)
    result = 100 / number
    print(f"Result: {result}")
except ValueError:
    print("Invalid number!")
except ZeroDivisionError:
    print("Cannot divide by zero!")
finally:
    print("Calculation attempt completed.")

Jika pengguna memasukkan "5":

Enter a number:
5
Result: 20.0
Calculation attempt completed.

Jika pengguna memasukkan "hello":

Enter a number:
hello
Invalid number!
Calculation attempt completed.

Jika pengguna memasukkan "0":

Enter a number:
0
Cannot divide by zero!
Calculation attempt completed.

Blok finally berjalan di ketiga kasus! Ini adalah perilaku kunci dari finally: ia selalu dieksekusi, terlepas dari apa yang terjadi di blok try.

Tidak

Ya

Ya

Tidak

Ya

Tidak

Mulai blok try

Exception di-raise?

Selesaikan blok try

Exception tertangkap?

Jalankan blok else jika ada

Jalankan blok except yang cocok

Exception menerus (propagate)

Jalankan blok finally

Exception tertangkap?

Lanjut setelah try-except-finally

Exception terus menerus (propagate)

25.3.3) Menggabungkan try, except, else, dan finally

Kamu bisa menggunakan keempat klausa sekaligus untuk membuat penanganan exception yang komprehensif:

python
# Struktur penanganan exception lengkap
print("Enter a number to calculate its reciprocal:")
user_input = input()
 
try:
    # Operasi yang berisiko
    number = int(user_input)
    reciprocal = 1 / number
except ValueError:
    # Tangani error konversi
    print("Error: Input must be a valid integer.")
except ZeroDivisionError:
    # Tangani pembagian dengan nol
    print("Error: Cannot calculate reciprocal of zero.")
else:
    # Kode yang hanya berjalan saat sukses
    print(f"The reciprocal of {number} is {reciprocal}")
    print(f"Verification: {number} × {reciprocal} = {number * reciprocal}")
finally:
    # Kode cleanup yang selalu berjalan
    print("Reciprocal calculation completed.")

Jika pengguna memasukkan "4":

Enter a number to calculate its reciprocal:
4
The reciprocal of 4 is 0.25
Verification: 4 × 0.25 = 1.0
Reciprocal calculation completed.

Jika pengguna memasukkan "hello":

Enter a number to calculate its reciprocal:
hello
Error: Input must be a valid integer.
Reciprocal calculation completed.

Jika pengguna memasukkan "0":

Enter a number to calculate its reciprocal:
0
Error: Cannot calculate reciprocal of zero.
Reciprocal calculation completed.

Alur eksekusinya adalah:

  1. Blok try selalu dieksekusi terlebih dahulu
  2. Jika exception terjadi, blok except yang cocok dieksekusi
  3. Jika tidak ada exception yang terjadi, blok else dieksekusi (jika ada)
  4. Blok finally selalu dieksekusi terakhir, terlepas dari apa yang terjadi

25.4) Me-raise Exception Secara Sengaja dengan raise

25.4.1) Mengapa Me-raise Exception?

Sejauh ini, kita menangkap exception yang Python raise secara otomatis. Tapi kadang-kadang kamu perlu me-raise exception secara sengaja di kode kamu sendiri. Ini berguna ketika:

  1. Kamu mendeteksi situasi tidak valid yang tidak bisa ditangani kode kamu
  2. Kamu ingin menegakkan aturan atau batasan
  3. Kamu ingin memberi sinyal error ke kode yang memanggil fungsi kamu

Me-raise exception adalah cara Python untuk mengatakan "Aku tidak bisa lanjut—ada yang salah, dan siapa pun yang memanggilku perlu menanganinya."

Sintaksnya sederhana: raise ExceptionType("error message")

Berikut contoh dasar:

python
# Me-raise exception secara sengaja
age = -5
 
if age < 0:
    raise ValueError("Age cannot be negative!")
 
print(f"Age: {age}")  # Baris ini tidak pernah dieksekusi

Output:

Traceback (most recent call last):
  File "example.py", line 5, in <module>
    raise ValueError("Age cannot be negative!")
ValueError: Age cannot be negative!

Saat Python menemui raise, Python langsung membuat exception dan mulai mencari blok except untuk menanganinya. Jika tidak ada, program berhenti dengan traceback.

25.4.2) Me-raise Exception di Fungsi

Me-raise exception sangat berguna di fungsi(function) untuk memvalidasi input dan menegakkan batasan:

python
# Fungsi yang memvalidasi input dengan me-raise exception
def calculate_discount(price, discount_percent):
    """Calculate discounted price.
    
    Args:
        price: Original price (must be positive)
        discount_percent: Discount percentage (must be 0-100)
    
    Returns:
        Discounted price
    
    Raises:
        ValueError: If inputs are invalid
    """
    if price < 0:
        raise ValueError("Price cannot be negative!")
    
    if discount_percent < 0 or discount_percent > 100:
        raise ValueError("Discount must be between 0 and 100!")
    
    discount_amount = price * (discount_percent / 100)
    return price - discount_amount
 
# Menggunakan fungsi
try:
    final_price = calculate_discount(100, 20)
    print(f"Final price: ${final_price}")
except ValueError as error:
    print(f"Error: {error}")

Output:

Final price: $80.0

Sekarang mari coba dengan input yang tidak valid:

python
# Harga tidak valid
try:
    final_price = calculate_discount(-50, 20)
    print(f"Final price: ${final_price}")
except ValueError as error:
    print(f"Error: {error}")

Output:

Error: Price cannot be negative!
python
# Diskon tidak valid
try:
    final_price = calculate_discount(100, 150)
    print(f"Final price: ${final_price}")
except ValueError as error:
    print(f"Error: {error}")

Output:

Error: Discount must be between 0 and 100!

Dengan me-raise exception, fungsi tersebut mengomunikasikan dengan jelas apa yang salah. Kode pemanggil kemudian bisa memutuskan cara menangani error—mungkin dengan meminta pengguna input baru, menggunakan nilai default, atau mencatat error.

25.4.3) Memilih Tipe Exception yang Tepat

Python memiliki banyak tipe exception bawaan, dan memilih yang tepat membuat kode kamu lebih jelas. Berikut exception yang paling sering dipakai untuk validasi:

  • ValueError: Gunakan saat nilainya bertipe benar tetapi nilainya tidak sesuai (misalnya umur negatif, persentase tidak valid)
  • TypeError: Gunakan saat nilainya bertipe benar-benar salah (misalnya string alih-alih angka)
  • KeyError: Gunakan saat key dictionary tidak ada
  • IndexError: Gunakan saat index sequence di luar rentang

Berikut contoh yang menunjukkan tipe exception yang berbeda:

python
# Menggunakan tipe exception yang sesuai
def get_student_grade(grades, student_name):
    """Get a student's grade from the grades dictionary.
    
    Args:
        grades: Dictionary mapping student names to grades
        student_name: Name of the student
    
    Returns:
        The student's grade
    
    Raises:
        TypeError: If grades is not a dictionary
        KeyError: If student_name is not in grades
        ValueError: If the grade is invalid
    """
    if not isinstance(grades, dict):
        raise TypeError("Grades must be a dictionary!")
    
    if student_name not in grades:
        raise KeyError(f"Student '{student_name}' not found!")
    
    grade = grades[student_name]
    
    if not (0 <= grade <= 100):
        raise ValueError(f"Invalid grade: {grade} (must be 0-100)")
    
    return grade
 
# Uji dengan data valid
grades = {"Alice": 95, "Bob": 87, "Carol": 92}
 
try:
    grade = get_student_grade(grades, "Alice")
    print(f"Alice's grade: {grade}")
except (TypeError, KeyError, ValueError) as error:
    print(f"Error: {error}")

Output:

Alice's grade: 95
python
# Uji dengan siswa yang tidak ada
try:
    grade = get_student_grade(grades, "David")
    print(f"David's grade: {grade}")
except (TypeError, KeyError, ValueError) as error:
    print(f"Error: {error}")

Output:

Error: Student 'David' not found!
python
# Uji dengan tipe yang salah
try:
    grade = get_student_grade("not a dict", "Alice")
    print(f"Alice's grade: {grade}")
except (TypeError, KeyError, ValueError) as error:
    print(f"Error: {error}")

Output:

Error: Grades must be a dictionary!

Menggunakan tipe exception yang sesuai membantu programmer lain (dan dirimu di masa depan) memahami jenis error yang terjadi.

25.4.4) Me-raise Kembali Exception (Re-raising)

Terkadang kamu ingin menangkap exception, melakukan sesuatu (seperti logging), lalu membiarkan exception itu terus menerus (propagate). Kamu bisa melakukan ini dengan menggunakan raise tanpa argumen apa pun di dalam blok except:

python
# Me-raise kembali exception setelah logging
def divide_numbers(a, b):
    """Divide two numbers with error logging."""
    try:
        result = a / b
        return result
    except ZeroDivisionError:
        print("ERROR LOG: Division by zero attempted")
        print(f"  Numerator: {a}, Denominator: {b}")
        raise  # Me-raise kembali exception yang sama
 
# Menggunakan fungsi
try:
    result = divide_numbers(10, 0)
    print(f"Result: {result}")
except ZeroDivisionError:
    print("Cannot divide by zero!")

Output:

ERROR LOG: Division by zero attempted
  Numerator: 10, Denominator: 0
Cannot divide by zero!

Statement raise tanpa argumen me-raise kembali exception yang baru saja ditangkap. Ini berguna ketika kamu ingin:

  1. Mencatat atau merekam error
  2. Melakukan cleanup
  3. Membiarkan error diteruskan ke pemanggil

25.4.5) Me-raise Exception dari Exception Lain

Terkadang kamu ingin me-raise exception baru sambil menangani exception lain, sambil mempertahankan konteks error aslinya. Python 3 menyediakan sintaks raise ... from ... untuk ini:

python
# Me-raise exception baru dari exception yang sudah ada
def load_config(config_dict, key):
    """Load configuration value from dictionary."""
    try:
        config_value = config_dict[key]
        
        # Coba parse sebagai integer
        parsed_value = int(config_value)
        return parsed_value
        
    except KeyError as error:
        raise RuntimeError(f"Configuration key missing: {key}") from error
    except ValueError as error:
        raise RuntimeError(f"Invalid configuration format for {key}") from error
 
# Menggunakan fungsi
config = {"timeout": "30", "retries": "5"}
 
try:
    value = load_config(config, "timeout")
    print(f"Config value: {value}")
except RuntimeError as error:
    print(f"Configuration error: {error}")
    print(f"Original cause: {error.__cause__}")

Output:

Config value: 30

Jika key tidak ada:

python
try:
    value = load_config(config, "missing_key")
    print(f"Config value: {value}")
except RuntimeError as error:
    print(f"Configuration error: {error}")
    print(f"Original cause: {error.__cause__}")

Output:

Configuration error: Configuration key missing: missing_key
Original cause: 'missing_key'

Keyword from menghubungkan exception baru dengan exception asli. Ini membuat rantai exception yang membantu debugging—kamu bisa melihat apa yang salah di level tinggi (configuration error) dan apa penyebab dasarnya (key tidak ditemukan).


Penanganan exception adalah salah satu alat terpenting untuk menulis program yang andal. Dengan menggunakan blok try-except, kamu bisa mengantisipasi masalah, menanganinya dengan elegan, dan memberikan pengalaman yang lebih baik untuk pengguna. Ingat:

  • Gunakan try-except untuk menangani error yang diperkirakan terjadi dengan elegan
  • Tangkap tipe exception yang spesifik daripada menggunakan bare except
  • Gunakan else untuk kode yang seharusnya hanya berjalan saat sukses
  • Gunakan finally untuk kode cleanup yang harus selalu berjalan
  • Me-raise exception di kode kamu sendiri untuk memberi sinyal adanya masalah
  • Pilih tipe exception yang sesuai agar error jelas
  • Berikan pesan error yang membantu yang menjelaskan apa yang salah

Di bab berikutnya, kita akan mempelajari teknik pemrograman defensif yang menggabungkan penanganan exception dengan validasi input dan strategi lain agar program kita lebih tangguh lagi.

© 2025. Primesoft Co., Ltd.
support@primesoft.ai