24. Memahami Error dan Traceback
Error adalah bagian yang tidak terhindarkan dalam pemrograman. Setiap programmer, dari pemula sampai ahli, mengalaminya secara rutin. Perbedaan antara kesulitan menghadapi error dan belajar darinya terletak pada pemahaman tentang apa yang Python coba sampaikan saat ada sesuatu yang salah.
Saat Python menemukan masalah di kode kamu, ia tidak hanya berhenti diam-diam—Python memberikan informasi detail tentang apa yang salah, di mana itu terjadi, dan sering kali memberi petunjuk tentang mengapa. Belajar membaca dan menafsirkan pesan error ini adalah salah satu skill paling berharga yang bisa kamu kembangkan sebagai programmer.
Di bab ini, kita akan membahas dua kategori utama error yang akan kamu temui: syntax error (masalah pada cara kamu menulis kode) dan runtime exception (masalah yang terjadi saat kode berjalan). Kita akan belajar membaca traceback—laporan error detail dari Python—dan memahami bagaimana exception mengubah alur normal program kamu. Yang paling penting, kita akan membangun mindset debugging yang memandang error bukan sebagai kegagalan, melainkan sebagai informasi berharga yang membantu kamu menulis kode yang lebih baik.
24.1) Syntax Error vs Runtime Exception
Python membedakan dua jenis masalah yang secara fundamental berbeda di kode kamu: syntax error dan runtime exception. Memahami perbedaan ini membantu kamu mendiagnosis masalah lebih cepat dan tahu harus mencari solusi di mana.
24.1.1) Apa Itu Syntax Error
Sebuah syntax error terjadi ketika Python tidak bisa memahami kode kamu karena melanggar aturan tata bahasa (grammar) bahasa tersebut. Sama seperti "The cat sat on the" adalah kalimat bahasa Inggris yang tidak lengkap, kode dengan syntax error adalah Python yang tidak lengkap atau salah format sehingga interpreter tidak bisa mem-parse-nya.
Syntax error terdeteksi sebelum program kamu berjalan. Python membaca seluruh skrip kamu terlebih dahulu, memastikan semuanya mengikuti aturan bahasa. Jika ia menemukan syntax error, Python menolak mengeksekusi bagian kode mana pun—bahkan bagian yang benar.
Berikut contoh sederhana:
# PERINGATAN: Syntax error - hanya untuk demonstrasi
# KESALAHAN: Tanda titik dua hilang setelah pernyataan if
age = 25
if age >= 18
print("You are an adult")Saat kamu mencoba menjalankan kode ini, Python langsung melaporkan:
File "example.py", line 3
if age >= 18
^
SyntaxError: expected ':'Perhatikan beberapa fitur penting dari pesan error ini:
- File dan nomor baris: Python memberi tahu kamu dengan tepat di mana ia menemukan masalah (
line 3) - Indikator visual: Tanda caret (
^) menunjukkan di mana Python mulai bingung - Jenis error:
SyntaxErrordengan jelas mengidentifikasi ini sebagai masalah tata bahasa - Petunjuk yang membantu:
expected ':'memberi tahu apa yang kurang
Kode tidak pernah berjalan karena Python bahkan tidak bisa mulai mengeksekusinya—syntax-nya tidak valid.
Mari lihat syntax error umum lainnya:
# PERINGATAN: Syntax error - hanya untuk demonstrasi
# KESALAHAN: Tanda kurung tidak berpasangan
numbers = [1, 2, 3, 4, 5]
total = sum(numbers
print(f"Total: {total}")Python melaporkan:
File "example.py", line 2
total = sum(numbers
^
SyntaxError: '(' was never closedDi sini, Python mendeteksi bahwa kita membuka tanda kurung pada baris 2 tetapi tidak pernah menutupnya. Error dilaporkan pada baris 2 (di tempat tanda kurung yang tidak ditutup berada), dan caret menunjuk ke tempat Python mengharapkan menemukan tanda kurung penutup.
Karakteristik utama syntax error:
- Terdeteksi sebelum kode apa pun berjalan
- Mencegah seluruh program dieksekusi
- Biasanya menunjukkan typo, tanda baca yang hilang, atau indentasi yang salah
- Lokasi error mungkin sedikit setelah kesalahan sebenarnya
24.1.2) Apa Itu Runtime Exception
Sebuah runtime exception (atau cukup "exception") terjadi ketika kode yang secara syntax benar menemukan masalah saat dieksekusi. Kodenya adalah Python yang valid secara tata bahasa, tetapi ada sesuatu yang salah saat program benar-benar berjalan.
Berbeda dengan syntax error, exception terjadi saat program kamu berjalan. Python berhasil mem-parse kode kamu dan mulai mengeksekusinya, tetapi kemudian menemukan situasi yang tidak bisa ditangani.
Berikut contoh sederhana:
# Kode ini syntax-nya valid tetapi akan memunculkan exception
numbers = [10, 20, 30]
print(numbers[0]) # Output: 10
print(numbers[5]) # Baris ini akan memunculkan IndexError
print("This line never executes")Output:
10
Traceback (most recent call last):
File "example.py", line 3, in <module>
print(numbers[5])
~~~~~~~^^^
IndexError: list index out of rangePerhatikan apa yang terjadi:
- Pernyataan
printpertama dieksekusi dengan sukses (kita melihat10) printkedua mencoba mengakses indeks 5, yang tidak ada- Python memunculkan exception
IndexError - Program berhenti, dan
printketiga tidak pernah dieksekusi
Kodenya benar secara syntax—Python tidak bermasalah memahami apa yang ingin kita lakukan. Masalah muncul saat eksekusi ketika kita mencoba mengakses elemen list yang tidak ada.
Berikut contoh lain yang menunjukkan jenis runtime exception yang berbeda:
# Syntax valid, tetapi pembagian dengan nol saat runtime
def calculate_average(total, count):
return total / count
# Ini berjalan baik
print(calculate_average(100, 4)) # Output: 25.0
print(calculate_average(75, 3)) # Output: 25.0
# Ini memunculkan exception
print(calculate_average(50, 0)) # ZeroDivisionErrorOutput:
25.0
25.0
Traceback (most recent call last):
File "example.py", line 8, in <module>
print(calculate_average(50, 0))
^^^^^^^^^^^^^^^^^^^^^^^^
File "example.py", line 2, in calculate_average
return total / count
~~~~~~^~~~~~~
ZeroDivisionError: division by zeroFungsi bekerja sempurna dua kali, tetapi pada pemanggilan ketiga, kita mengirim 0 sebagai count, menyebabkan pembagian dengan nol. Python tidak bisa mendeteksi masalah ini sampai kode benar-benar dijalankan dengan nilai tersebut.
Karakteristik utama runtime exception:
- Terjadi saat eksekusi program
- Kodenya valid secara syntax
- Sering bergantung pada data atau kondisi tertentu
- Program berjalan sampai titik di mana exception terjadi
- Input yang berbeda bisa memunculkan exception yang berbeda (atau tidak ada sama sekali)
24.1.3) Membandingkan Syntax Error dan Runtime Exception
Mari lihat kedua jenis error ini berdampingan untuk memahami perbedaannya:
# Contoh 1: Syntax Error
# KESALAHAN: Tanda kutip penutup hilang
print("Program started!")
message = "Hello, world
print(message)Ini langsung menghasilkan syntax error:
File "example.py", line 4
message = "Hello, world
^
SyntaxError: unterminated string literal (detected at line 4)Penting: Perhatikan bahwa kamu tidak melihat "Program started!" di output. Python mendeteksi syntax error sebelum menjalankan kode apa pun.
Sekarang bandingkan dengan runtime exception:
# Contoh 2: Runtime Exception
# Syntax valid, tetapi variabelnya tidak ada
print("Program started!")
message = "Hello, world"
print(mesage) # Typo: 'mesage' bukannya 'message'Output:
Program started!
Traceback (most recent call last):
File "example.py", line 5, in <module>
print(mesage)
^^^^^^
NameError: name 'mesage' is not definedPenting: Kali ini kamu melihat "Program started!" di output. Python berhasil menjalankan dua print pertama dan pernyataan assignment (baris 3-4), tetapi menemukan masalah pada baris 5 saat mencoba mencari mesage.
Perbedaan utamanya: Pada contoh pertama, Python bahkan tidak pernah mencoba menjalankan kode—Python menemukan syntax error saat parsing. Pada contoh kedua, Python berhasil mulai mengeksekusi program dan menjalankan beberapa baris sebelum menemukan runtime error.
24.2) Jenis Exception Bawaan yang Umum
Python punya banyak jenis exception bawaan, masing-masing mewakili jenis masalah tertentu. Belajar mengenali exception yang umum ini membantu kamu cepat memahami apa yang salah dan cara memperbaikinya. Setiap jenis exception punya nama deskriptif yang memberi petunjuk tentang masalahnya.
24.2.1) NameError: Menggunakan Nama yang Belum Didefinisikan
Sebuah NameError terjadi ketika kamu mencoba menggunakan variabel, fungsi, atau nama lain yang tidak dikenali Python. Ini biasanya berarti kamu lupa mendefinisikan sesuatu, salah mengeja nama, atau mencoba menggunakan sesuatu sebelum dibuat.
# Contoh 1: Lupa mendefinisikan variabel
print(greeting) # NameError: name 'greeting' is not definedOutput:
Traceback (most recent call last):
File "example.py", line 2, in <module>
print(greeting)
^^^^^^^^
NameError: name 'greeting' is not definedPython memberi tahu bahwa ia tidak tahu apa itu greeting. Kamu perlu membuatnya terlebih dahulu:
# Versi yang benar
greeting = "Hello, Python!"
print(greeting) # Output: Hello, Python!Berikut contoh yang lebih halus dengan typo:
# Contoh 2: Typo pada nama variabel
user_name = "Alice"
age = 30
print(f"{username} is {age} years old") # NameError: name 'username' is not definedKita mendefinisikan user_name (dengan underscore) tetapi mencoba menggunakan username (tanpa underscore). Python menganggap ini sebagai nama yang sepenuhnya berbeda.
24.2.2) TypeError: Tipe yang Salah untuk Suatu Operasi
Sebuah TypeError terjadi ketika kamu mencoba melakukan operasi pada nilai dengan tipe yang salah. Misalnya, kamu tidak bisa menambahkan string ke integer, atau memanggil sesuatu yang bukan fungsi.
# Contoh 1: Mencampur tipe yang tidak kompatibel
age = 25
message = "You are " + age + " years old" # TypeErrorOutput:
Traceback (most recent call last):
File "example.py", line 2, in <module>
message = "You are " + age + " years old"
~~~~~~~~~~~~^~~~~
TypeError: can only concatenate str (not "int") to strPython memberi tahu bahwa operator + bisa menggabungkan string dengan string, tetapi tidak string dengan integer. Kamu perlu mengonversi integer ke string:
# Versi yang benar
age = 25
message = "You are " + str(age) + " years old"
print(message) # Output: You are 25 years oldTypeError juga terjadi ketika kamu memberikan jumlah argumen yang salah ke sebuah fungsi:
# Contoh 3: Jumlah argumen salah
def calculate_area(length, width):
return length * width
area = calculate_area(5) # TypeError: missing 1 required positional argumentOutput:
Traceback (most recent call last):
File "example.py", line 4, in <module>
area = calculate_area(5)
TypeError: calculate_area() missing 1 required positional argument: 'width'Fungsi mengharapkan dua argumen, tetapi kita hanya memberikan satu.
24.2.3) ValueError: Tipe Benar, Nilainya Salah
Sebuah ValueError terjadi ketika kamu memberikan nilai dengan tipe yang benar, tetapi nilai itu sendiri tidak sesuai untuk operasi tersebut. Tipenya tepat, tetapi nilai spesifiknya tidak masuk akal dalam konteks itu.
# Contoh 1: Mengonversi string tidak valid menjadi integer
user_input = "twenty-five"
age = int(user_input) # ValueError: invalid literal for int()Output:
Traceback (most recent call last):
File "example.py", line 2, in <module>
age = int(user_input)
ValueError: invalid literal for int() with base 10: 'twenty-five'Fungsi int() mengharapkan string, dan kita memberinya string—jadi tipenya benar. Namun string "twenty-five" tidak bisa dikonversi menjadi integer karena berisi huruf. String "25" akan bekerja baik:
# Versi yang benar
user_input = "25"
age = int(user_input)
print(age) # Output: 25ValueError juga terjadi pada method list:
# Contoh 3: Menghapus item yang tidak ada
fruits = ["apple", "banana", "orange"]
fruits.remove("grape") # ValueError: 'grape' is not in listOutput:
Traceback (most recent call last):
File "example.py", line 2, in <module>
fruits.remove("grape")
~~~~~~~~~~~~~^^^^^^^^^
ValueError: list.remove(x): x not in listMethod remove() mengharapkan nilai yang memang ada di dalam list. Kita sebaiknya mengecek dulu:
# Versi yang benar
fruits = ["apple", "banana", "orange"]
if "grape" in fruits:
fruits.remove("grape")
else:
print("Grape not found in list") # Output: Grape not found in list24.2.4) IndexError: Indeks Sequence Tidak Valid
Sebuah IndexError terjadi ketika kamu mencoba mengakses sequence (list, tuple, string) menggunakan indeks yang tidak ada. Ingat bahwa Python memakai indeks berbasis nol, dan indeks valid berada pada rentang 0 sampai len(sequence) - 1.
# Contoh 1: Indeks terlalu besar
colors = ["red", "green", "blue"]
print(colors[0]) # Output: red
print(colors[3]) # IndexError: list index out of rangeOutput:
red
Traceback (most recent call last):
File "example.py", line 3, in <module>
print(colors[3])
~~~~~~^^^
IndexError: list index out of rangeList memiliki tiga elemen pada indeks 0, 1, dan 2. Indeks 3 tidak ada. Ini kesalahan yang sangat umum saat kamu lupa bahwa indeks dimulai dari 0:
# Versi yang benar
colors = ["red", "green", "blue"]
print(colors[2]) # Output: blue (elemen ketiga)24.2.5) KeyError: Key Dictionary Tidak Ada
Sebuah KeyError terjadi ketika kamu mencoba mengakses key dictionary yang tidak ada. Tidak seperti list di mana kamu bisa mengecek panjangnya, dictionary bisa punya key apa pun, jadi kamu perlu memastikan key itu ada sebelum mengaksesnya.
# Contoh 1: Mengakses key yang tidak ada
student = {
"name": "Alice",
"age": 20,
"major": "Computer Science"
}
print(student["name"]) # Output: Alice
print(student["grade"]) # KeyError: 'grade'Output:
Alice
Traceback (most recent call last):
File "example.py", line 7, in <module>
print(student["grade"])
~~~~~~~^^^^^^^^^
KeyError: 'grade'Dictionary tidak punya key "grade". Kamu bisa mengecek apakah key ada terlebih dahulu:
# Versi yang benar menggunakan 'in'
student = {
"name": "Alice",
"age": 20,
"major": "Computer Science"
}
if "grade" in student:
print(student["grade"])
else:
print("Grade not available") # Output: Grade not availableAtau gunakan method get(), yang mengembalikan None (atau nilai default) alih-alih memunculkan error:
# Alternatif menggunakan get()
grade = student.get("grade")
if grade is not None:
print(f"Grade: {grade}")
else:
print("Grade not available") # Output: Grade not availableKeyError sering terjadi saat memproses data dengan struktur yang tidak konsisten:
# Contoh 2: Memproses banyak record
students = [
{"name": "Alice", "age": 20, "grade": "A"},
{"name": "Bob", "age": 21}, # Key 'grade' tidak ada
{"name": "Carol", "age": 19, "grade": "B"}
]
for student in students:
print(f"{student['name']}: {student['grade']}") # KeyError pada BobOutput:
Alice: A
Traceback (most recent call last):
File "example.py", line 7, in <module>
print(f"{student['name']}: {student['grade']}")
~~~~~~~^^^^^^^^^
KeyError: 'grade'Gunakan get() dengan nilai default untuk menangani key yang hilang dengan rapi:
# Versi yang benar
students = [
{"name": "Alice", "age": 20, "grade": "A"},
{"name": "Bob", "age": 21},
{"name": "Carol", "age": 19, "grade": "B"}
]
for student in students:
grade = student.get("grade", "Not assigned")
print(f"{student['name']}: {grade}")Output:
Alice: A
Bob: Not assigned
Carol: B24.2.6) AttributeError: Akses Atribut yang Tidak Valid
Sebuah AttributeError terjadi ketika kamu mencoba mengakses atribut atau method yang tidak ada pada sebuah objek. Ini sering terjadi ketika kamu tertukar method antar tipe yang berbeda atau salah mengeja nama atribut.
# Contoh 1: Method yang salah untuk tipe tersebut
numbers = [1, 2, 3, 4, 5]
numbers.append(6) # Ini berfungsi - list punya append()
print(numbers) # Output: [1, 2, 3, 4, 5, 6]
text = "Hello"
text.append("!") # AttributeError: 'str' object has no attribute 'append'Output:
[1, 2, 3, 4, 5, 6]
Traceback (most recent call last):
File "example.py", line 6, in <module>
text.append("!")
^^^^^^^^^^^
AttributeError: 'str' object has no attribute 'append'String tidak punya method append() karena string bersifat immutable. Kamu perlu memakai concatenation atau method string lainnya:
# Versi yang benar
text = "Hello"
text = text + "!" # Concatenation
print(text) # Output: Hello!AttributeError juga terjadi karena typo:
# Contoh 2: Salah eja nama method
message = "Python Programming"
result = message.uppper() # AttributeError: 'str' object has no attribute 'uppper'Output:
Traceback (most recent call last):
File "example.py", line 2, in <module>
result = message.uppper()
^^^^^^^^^^^^^^
AttributeError: 'str' object has no attribute 'uppper'. Did you mean: 'upper'?Perhatikan bahwa Python 3.10+ sering menyarankan ejaan yang benar! Method yang benar adalah upper():
# Versi yang benar
message = "Python Programming"
result = message.upper()
print(result) # Output: PYTHON PROGRAMMING24.2.7) ZeroDivisionError: Pembagian dengan Nol
Sebuah ZeroDivisionError terjadi ketika kamu mencoba membagi angka dengan nol, yang secara matematika tidak terdefinisi. Ini sering terjadi pada input pengguna atau nilai hasil perhitungan yang tidak kamu duga menjadi nol.
# Contoh 1: Pembagian langsung dengan nol
result = 10 / 0 # ZeroDivisionError: division by zeroOutput:
Traceback (most recent call last):
File "example.py", line 1, in <module>
result = 10 / 0
~~~^~~
ZeroDivisionError: division by zeroIni juga berlaku untuk floor division dan modulo:
# Contoh 2: Operasi pembagian lainnya
a = 10 // 0 # ZeroDivisionError
b = 10 % 0 # ZeroDivisionErrorContoh yang lebih realistis melibatkan perhitungan:
# Contoh 3: Menghitung rata-rata
def calculate_average(numbers):
total = sum(numbers)
count = len(numbers)
return total / count
scores = [85, 90, 78, 92]
print(calculate_average(scores)) # Output: 86.25
empty_scores = []
print(calculate_average(empty_scores)) # ZeroDivisionErrorOutput:
86.25
Traceback (most recent call last):
File "example.py", line 9, in <module>
print(calculate_average(empty_scores))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "example.py", line 4, in calculate_average
return total / count
~~~~~~^~~~~~~
ZeroDivisionError: division by zeroList kosong punya panjang 0, menyebabkan pembagian dengan nol. Selalu cek kondisi ini:
# Versi yang benar
def calculate_average(numbers):
if len(numbers) == 0:
return 0 # Atau return None, atau raise error yang lebih deskriptif
total = sum(numbers)
count = len(numbers)
return total / count
scores = [85, 90, 78, 92]
print(calculate_average(scores)) # Output: 86.25
empty_scores = []
print(calculate_average(empty_scores)) # Output: 0Memahami jenis exception umum ini membantu kamu mendiagnosis masalah dengan cepat. Saat kamu melihat sebuah exception, nama tipenya langsung memberi tahu kategori masalah yang terjadi, dan pesan error memberikan detail spesifik tentang apa yang salah.
24.3) Membaca dan Menafsirkan Traceback Secara Detail
Saat runtime exception terjadi, Python tidak hanya memberi tahu apa yang salah—Python memberikan traceback yang detail, menunjukkan dengan tepat bagaimana program kamu sampai ke titik itu. Belajar membaca traceback sangat penting untuk debugging yang efektif. Traceback itu seperti jejak remah roti yang menunjukkan jalur yang ditempuh program kamu sebelum menemui error.
24.3.1) Anatomi Traceback
Mari mulai dengan contoh sederhana dan periksa setiap bagian traceback:
# Program sederhana dengan error
def calculate_discount(price, discount_percent):
discount_amount = price * (discount_percent / 100)
final_price = price - discount_amount
return final_price
def process_order(item_price, discount):
discounted_price = calculate_discount(item_price, discount)
tax = discounted_price * 0.08
total = discounted_price + tax
return total
# Program utama
original_price = "50" # Ups! Ini seharusnya angka
discount_rate = 10
final_cost = process_order(original_price, discount_rate)
print(f"Final cost: ${final_cost:.2f}")Output:
Traceback (most recent call last):
File "example.py", line 16, in <module>
final_cost = process_order(original_price, discount_rate)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "example.py", line 8, in process_order
discounted_price = calculate_discount(item_price, discount)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "example.py", line 2, in calculate_discount
discount_amount = price * (discount_percent / 100)
~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~
TypeError: can't multiply sequence by non-int of type 'float'Mari uraikan setiap komponen traceback ini:
1. Header: "Traceback (most recent call last):"
Baris ini memberi tahu kamu bahwa yang berikutnya adalah traceback—catatan pemanggilan fungsi. Frasa "most recent call last" berarti traceback ditampilkan dalam urutan kronologis: fungsi yang dipanggil pertama muncul di awal, dan lokasi tempat error benar-benar terjadi muncul terakhir.
2. Call Stack (Dibaca dari Atas ke Bawah):
File "example.py", line 16, in <module>
final_cost = process_order(original_price, discount_rate)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^Ini adalah pemanggilan fungsi pertama dalam rantai. Ini menunjukkan:
- Nama file:
"example.py"- lokasi kode berada - Nomor baris:
line 16- baris persis yang melakukan pemanggilan ini - Konteks:
in <module>- kode ini berada di level teratas (bukan di dalam fungsi) - Kode: Baris sebenarnya yang dieksekusi
- Highlight: Karakter
^menunjuk ke bagian spesifik dari baris yang terlibat
Konteks <module> berarti ini adalah kode yang berjalan di level module (bagian utama dari skrip kamu), bukan di dalam fungsi mana pun.
File "example.py", line 8, in process_order
discounted_price = calculate_discount(item_price, discount)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^Ini adalah pemanggilan fungsi kedua. Fungsi process_order dipanggil dari baris 16, dan sekarang kita berada di dalam fungsi itu pada baris 8, di mana ia memanggil calculate_discount.
File "example.py", line 2, in calculate_discount
discount_amount = price * (discount_percent / 100)
~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~Di sinilah error sebenarnya terjadi. Sekarang kita berada di dalam fungsi calculate_discount pada baris 2, dan inilah baris yang menyebabkan masalah.
3. Pesan Error:
TypeError: can't multiply sequence by non-int of type 'float'Ini adalah error sebenarnya yang terjadi:
- Jenis exception:
TypeError- memberi tahu kategori error - Deskripsi: Sisanya menjelaskan secara spesifik apa yang salah
Dalam kasus ini, Python memberi tahu bahwa kita mencoba mengalikan sebuah sequence (string, dalam kasus ini) dengan float, yang tidak diizinkan.
24.3.2) Membaca Traceback dari Bawah ke Atas
Walaupun traceback dicetak dalam urutan kronologis (atas ke bawah), programmer berpengalaman sering membacanya dari bawah ke atas karena error sebenarnya ada di bagian bawah, dan baris-baris di atas menunjukkan bagaimana kita sampai ke sana.
Mari baca traceback sebelumnya dari bawah ke atas:
Langkah 1: Mulai dari pesan error
TypeError: can't multiply sequence by non-int of type 'float'"Oke, kita mencoba mengalikan sebuah sequence dengan float. Itu tidak diizinkan."
Langkah 2: Lihat di mana error terjadi
File "example.py", line 2, in calculate_discount
discount_amount = price * (discount_percent / 100)
~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~"Error terjadi di fungsi calculate_discount pada baris 2. Kita mencoba mengalikan price dengan sesuatu."
Langkah 3: Telusuri mundur untuk melihat bagaimana kita sampai di sana
File "example.py", line 8, in process_order
discounted_price = calculate_discount(item_price, discount)"Fungsi calculate_discount dipanggil dari process_order pada baris 8, dengan item_price dikirim sebagai parameter price."
Langkah 4: Lanjutkan menelusuri mundur
File "example.py", line 16, in <module>
final_cost = process_order(original_price, discount_rate)"Dan process_order dipanggil dari program utama pada baris 16, dengan original_price dikirim sebagai item_price."
Langkah 5: Temukan akar masalahnya
Sekarang kita bisa menelusuri masalahnya: original_price adalah "50" (sebuah string), yang diteruskan sebagai item_price ke process_order, lalu diteruskan sebagai price ke calculate_discount, tempat kita mencoba mengalikannya dengan float. Solusinya adalah menjadikan original_price sebagai angka:
# Versi yang diperbaiki
def calculate_discount(price, discount_percent):
discount_amount = price * (discount_percent / 100)
final_price = price - discount_amount
return final_price
def process_order(item_price, discount):
discounted_price = calculate_discount(item_price, discount)
tax = discounted_price * 0.08
total = discounted_price + tax
return total
# Program utama - memperbaiki tipe datanya
original_price = 50 # Sekarang ini angka, bukan string
discount_rate = 10
final_cost = process_order(original_price, discount_rate)
print(f"Final cost: ${final_cost:.2f}") # Output: Final cost: $48.6024.3.3) Merangkum: Menjadikan Traceback sebagai Alat Debugging
Memahami cara membaca traceback mengubahnya dari dinding teks yang mengintimidasi menjadi alat debugging yang membantu. Setiap baris memberi informasi berharga tentang jalur eksekusi program kamu, dan dengan latihan, kamu akan bisa cepat mengidentifikasi dan memperbaiki masalah dengan mengikuti petunjuk dari traceback.
24.4) Bagaimana Exception Mengubah Alur Normal Program
Saat sebuah exception terjadi, ia tidak hanya menghentikan program kamu—ia secara fundamental mengubah cara program dieksekusi. Memahami perilaku ini sangat penting untuk menulis kode yang tangguh dan untuk memahami apa yang terjadi saat error muncul.
24.4.1) Alur Program Normal vs Alur Exception
Dalam eksekusi normal, Python menjalankan kode kamu baris demi baris, dari atas ke bawah:
# Alur program normal
print("Step 1: Starting calculation")
result = 10 + 5
print(f"Step 2: Result is {result}")
final = result * 2
print(f"Step 3: Final value is {final}")
print("Step 4: Program complete")Output:
Step 1: Starting calculation
Step 2: Result is 15
Step 3: Final value is 30
Step 4: Program completeSetiap baris dieksekusi berurutan. Sekarang mari lihat apa yang terjadi saat exception terjadi:
# Alur program dengan exception
print("Step 1: Starting calculation")
result = 10 / 0 # Ini memunculkan ZeroDivisionError
print(f"Step 2: Result is {result}") # Ini tidak pernah dieksekusi
final = result * 2 # Ini tidak pernah dieksekusi
print(f"Step 3: Final value is {final}") # Ini tidak pernah dieksekusi
print("Step 4: Program complete") # Ini tidak pernah dieksekusiOutput:
Step 1: Starting calculation
Traceback (most recent call last):
File "example.py", line 2, in <module>
result = 10 / 0
~~~^~~
ZeroDivisionError: division by zeroPerhatikan bahwa hanya pernyataan print pertama yang dieksekusi. Begitu exception terjadi pada baris 2, Python berhenti mengeksekusi sisa kode. Exception menginterupsi alur normal.
24.4.2) Exception Merambat Naik pada Call Stack
Saat exception terjadi di dalam sebuah fungsi, Python tidak hanya berhenti pada fungsi itu—Python akan merambat (naik) melalui call stack sampai ada sesuatu yang menanganinya atau program berhenti.
# Contoh 1: Exception merambat melalui fungsi-fungsi
def calculate_average(numbers):
total = sum(numbers)
count = len(numbers)
return total / count # Bisa memunculkan ZeroDivisionError
def process_scores(score_list):
print("Processing scores...")
avg = calculate_average(score_list)
print(f"Average calculated: {avg}")
return avg
def main():
print("Program starting")
scores = [] # List kosong
result = process_scores(scores)
print(f"Final result: {result}")
print("Program ending")
main()Output:
Program starting
Processing scores...
Traceback (most recent call last):
File "example.py", line 18, in <module>
main()
File "example.py", line 14, in main
result = process_scores(scores)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "example.py", line 9, in process_scores
avg = calculate_average(score_list)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "example.py", line 4, in calculate_average
return total / count
~~~~~~^~~~~~~
ZeroDivisionError: division by zeroMari telusuri apa yang terjadi:
main()mulai berjalan dan mencetak "Program starting"main()memanggilprocess_scores()process_scores()mencetak "Processing scores..."process_scores()memanggilcalculate_average()calculate_average()mencoba membagi dengan nol- Exception terjadi dan merambat naik:
calculate_average()berhenti seketika (tidak mengembalikan nilai)- Kontrol kembali ke
process_scores(), tetapi bukan secara normal—exception terus merambat process_scores()berhenti seketika (print setelahcalculate_average()tidak pernah dieksekusi)- Kontrol kembali ke
main(), tetapi lagi-lagi exception terus merambat main()berhenti seketika (print setelahprocess_scores()tidak pernah dieksekusi)
- Program berhenti dengan traceback
Tidak ada kode setelah exception yang dieksekusi di fungsi mana pun. Exception “menggelembung naik” melalui semua pemanggilan fungsi sampai mencapai level teratas dan menghentikan program.
24.5) Mindset Debugging: Menganggap Error sebagai Informasi, Bukan Kegagalan
Salah satu skill paling penting dalam pemrograman bukan menulis kode yang sempurna—melainkan belajar bekerja secara efektif dengan kode yang tidak sempurna. Setiap programmer, apa pun level pengalamannya, menulis kode yang menghasilkan error. Perbedaan antara programmer yang kesulitan dan yang efektif bukan pada menghindari error, tetapi pada bagaimana mereka meresponsnya.
24.5.1) Error Bukanlah Kegagalan
Saat kamu belajar pemrograman, wajar kalau kamu merasa frustrasi ketika menemui error. Kamu mungkin merasa kamu melakukan sesuatu yang salah atau kamu “nggak paham-paham.” Mindset ini tidak produktif dan, yang lebih penting, tidak akurat.
Error bukanlah kegagalan—error adalah feedback.
Anggap error seperti GPS yang menghitung ulang rute kamu. Saat kamu salah belok, GPS tidak bilang “Kamu gagal!” Ia bilang “Recalculating route” dan memberi arahan baru. Pesan error Python bekerja dengan cara yang sama: mereka memberi tahu bahwa jalur yang kamu ambil tidak berhasil, dan mereka memberikan informasi untuk membantu kamu menemukan jalur yang berhasil.
Pertimbangkan contoh sederhana ini:
# Percobaan pertama menghitung rata-rata
def calculate_average(numbers):
total = sum(numbers)
average = total / len(numbers)
return average
scores = []
result = calculate_average(scores)
print(f"Average: {result}")Output:
Traceback (most recent call last):
File "example.py", line 8, in <module>
result = calculate_average(scores)
^^^^^^^^^^^^^^^^^^^^^^^^^
File "example.py", line 4, in calculate_average
average = total / len(numbers)
~~~~~~^~~~~~~~~~~~~~
ZeroDivisionError: division by zeroError ini tidak mengatakan bahwa kamu programmer yang buruk. Ia mengatakan sesuatu yang spesifik dan berguna: “Kamu mencoba membagi dengan nol, yang terjadi saat list kosong. Kamu perlu menangani kasus itu.”
Dengan informasi ini, kamu bisa memperbaiki kode kamu:
# Versi yang ditingkatkan berdasarkan feedback error
def calculate_average(numbers):
if len(numbers) == 0:
return 0 # Atau return None, atau raise error yang lebih deskriptif
total = sum(numbers)
average = total / len(numbers)
return average
scores = []
result = calculate_average(scores)
print(f"Average: {result}") # Output: Average: 0Error tersebut membantu kamu menulis kode yang lebih baik. Tanpa error itu, kamu mungkin tidak menyadari bahwa fungsi kamu tidak bisa menangani list kosong.
24.5.2) Setiap Error Mengajarkan Sesuatu
Setiap error yang kamu temui mengajarkan sesuatu tentang Python, tentang kode kamu, atau tentang pemrograman secara umum. Mari lihat beberapa contoh tentang apa yang diajarkan oleh berbagai error:
Contoh 1: Belajar tentang tipe
# Mencoba menambahkan tipe yang tidak kompatibel
age = 25
message = "You are " + age + " years old"Output:
TypeError: can only concatenate str (not "int") to strApa yang ini ajarkan: Python punya aturan tipe yang ketat. Kamu tidak bisa mencampur string dan angka dalam penggabungan. Error ini mengajarkan kompatibilitas tipe dan mengenalkan konsep konversi tipe.
Contoh 2: Belajar tentang struktur data
# Mencoba mengakses dictionary seperti list
student = {"name": "Alice", "age": 20}
first_value = student[0]Output:
KeyError: 0Apa yang ini ajarkan: Dictionary menggunakan key, bukan indeks numerik. Error ini mengajarkan perbedaan antara dictionary dan list, serta cara mengakses nilai dictionary dengan benar.
Contoh 3: Belajar tentang scope
# Mencoba menggunakan variabel sebelum mendefinisikannya
def greet():
print(f"Hello, {name}!")
greet()
name = "Alice"Output:
NameError: name 'name' is not definedApa yang ini ajarkan: Variabel harus didefinisikan sebelum digunakan, dan urutan eksekusi itu penting. Error ini mengajarkan scope variabel dan pentingnya inisialisasi.
Setiap error ini memberikan informasi spesifik dan bisa ditindaklanjuti yang membantu kamu memahami Python lebih baik. Alih-alih melihatnya sebagai hambatan, anggap ini sebagai kesempatan belajar.
24.5.3) Merangkul Mindset Debugging
Programmer profesional menghabiskan porsi waktu yang signifikan untuk debugging. Ini bukan tanda kelemahan—ini bagian inti dari pekerjaan. Programmer terbaik bukan yang tidak pernah membuat kesalahan; mereka adalah yang:
- Mengharapkan error: Mereka tahu error akan terjadi dan tidak terkejut atau patah semangat
- Membaca error dengan teliti: Mereka mengambil informasi sebanyak mungkin dari pesan error
- Debug secara sistematis: Mereka mengikuti proses logis alih-alih membuat perubahan acak
- Belajar dari error: Mereka menjadikan setiap error sebagai kesempatan untuk memahami Python lebih baik
- Tetap penasaran: Mereka bertanya “Kenapa itu bisa terjadi?” alih-alih hanya “Gimana cara memperbaikinya?”
Ingat: Setiap error adalah kesempatan untuk mempelajari sesuatu yang baru tentang Python, tentang pemrograman, atau tentang pemecahan masalah. Rangkul error sebagai feedback yang berharga, hadapi secara sistematis, dan rayakan keberhasilan debugging kamu. Mindset ini akan bermanfaat sepanjang perjalanan pemrograman kamu.
Memahami error dan traceback adalah hal yang mendasar untuk menjadi programmer Python yang efektif. Di bab ini, kita sudah belajar membedakan syntax error (masalah pada struktur kode) dan runtime exception (masalah saat eksekusi), mengenali jenis exception yang umum dan apa yang ditunjukkannya, membaca dan menafsirkan traceback detail untuk menemukan akar masalah, memahami bagaimana exception mengubah alur program dengan merambat naik pada call stack, dan membangun mindset debugging yang memperlakukan error sebagai informasi berharga alih-alih kegagalan.
Skill ini menjadi fondasi untuk bab berikutnya, di mana kita akan belajar menangani exception dengan baik menggunakan blok try dan except, sehingga program kita bisa pulih dari error dan tetap berjalan. Namun sebelum kita bisa menangani exception secara efektif, kita perlu memahaminya secara menyeluruh—dan itulah yang sudah kita lakukan di sini.