Lewati ke isi
Profil penulisSeri Buku GIS Kehutanan dan Pertanian/ PY

BAB 10: Membaca Petunjuk Komputer: Galat dan Cara Menanganinya

Studi kasus: "Lima berkas kiriman tim lapangan" #

Tim lapangan mengirim lima berkas data. Anda menjalankan skrip pembaca, dan skrip berhenti di berkas ketiga dengan tulisan merah panjang. Dua berkas sisanya tidak sempat dibaca. Kepala Seksi bertanya: "Mengapa satu berkas rusak menghentikan semuanya?" Jawabannya: skrip Anda belum siap menghadapi galat. Bab ini mengajarkan cara membaca galat dan cara menanganinya dengan tenang.

Konsep: galat dalam tiga kalimat #

Galat (error) adalah pesan dari Python bahwa ia tidak bisa melanjutkan, lengkap dengan petunjuk letak dan sebabnya. Bayangkan petugas yang menolak berkas dengan catatan: "halaman 3, baris 5: tanda tangan kurang". Dengan try/except, Anda memberi petugas itu instruksi: "bila ada berkas bermasalah, catat lalu lanjutkan ke berkas berikutnya".

Ilustrasi 10.1: Cara membaca pesan galat
Skema pesan galat Python berisi judul Traceback, nama berkas dan nomor baris, baris penyebab, dan jenis galat; dibaca mulai dari baris paling bawah

Istilah baru di bagian ini:

  • Galat (exception): peristiwa yang menghentikan skrip.
  • Traceback: tulisan jejak galat. Dibaca dari baris paling bawah ke atas.
  • try dan except: coba kerjakan, dan bila galat, tangkap lalu tangani.
  • raise: perintah untuk menimbulkan galat sendiri dengan pesan yang jelas.
  • assert: perintah memastikan sebuah syarat terpenuhi.

Membaca pesan galat #

Simpan tiga baris ini sebagai rata.py, lalu jalankan:

PYTHONUnduh berkas
nilai = []
print("Menghitung rata-rata")
print(sum(nilai) / len(nilai))

Python menampilkan:

TERMINAL
Menghitung rata-rata
Traceback (most recent call last):
  File "D:\Latihan\rata.py", line 3, in <module>
    print(sum(nilai) / len(nilai))
          ~~~~~~~~~~~^~~~~~~~~~~~
ZeroDivisionError: division by zero

Baris pertama (Menghitung rata-rata) adalah hasil print sebelum galat. Alamat berkas di komputer Anda akan berbeda. Cara membacanya, dari bawah ke atas:

  1. Baris paling bawah menyebut jenis galat dan alasannya: ZeroDivisionError: division by zero. Artinya: ada pembagian dengan nol.
  2. Baris bergaris ~~~^~~~ menandai bagian kode yang bermasalah, tepat di bawah baris penyebab: print(sum(nilai) / len(nilai)).
  3. Baris File ... menyebut nama berkas dan nomor baris: berkas rata.py, baris 3.

Kini Anda bisa langsung ke baris 3 dan bertanya: mengapa len(nilai) bernilai nol? Karena daftar nilai kosong.

Jenis galat yang paling sering muncul #

Jenis galatContoh pesanPenyebab umum
IndexErrorlist index out of rangeNomor daftar melebihi isi
KeyError'B'Kunci kamus tidak ada
NameErrorname 'Nama' is not definedSalah ketik atau huruf besar-kecil
TypeErrorcan only concatenate str (not "float") to strMencampur teks dengan angka
ValueErrorinvalid literal for int() with base 10: 'empat'Isi tidak bisa diubah ke jenis yang diminta
FileNotFoundErrorNo such file or directoryAlamat berkas salah
ModuleNotFoundErrorNo module named 'qgis'Modul belum terpasang atau salah penjalan
IndentationErrorexpected an indented blockJarak menjorok salah
ZeroDivisionErrordivision by zeroPembagi bernilai nol

Menangani galat: try dan except #

PYTHONUnduh berkas
def persen(bagian, total):
    try:
        return 100 * bagian / total
    except ZeroDivisionError:
        return None


print("Persen 3 dari 12:", persen(3, 12))
print("Persen 3 dari 0:", persen(3, 0))

Penjelasannya:

  • Python mencoba isi try lebih dulu.
  • Bila tidak ada galat, hasil dikembalikan seperti biasa.
  • Bila terjadi ZeroDivisionError, Python melompat ke blok except yang cocok. Di sini jawabannya None, artinya "tidak ada jawaban yang masuk akal".
  • Skrip tidak berhenti. Baris print berikutnya tetap berjalan.
HASIL
Persen 3 dari 12: 25.0
Persen 3 dari 0: None

Blok lengkapnya punya empat bagian:

  • try: yang dicoba.
  • except JenisGalat: ditangani bila galat itu muncul. Boleh ada beberapa except.
  • else: berjalan hanya bila tidak ada galat.
  • finally: selalu berjalan, galat atau tidak. Cocok untuk merapikan.

Menimbulkan galat sendiri: raise dan assert #

PYTHONUnduh berkas
def luas_ke_hektar(m2):
    if m2 < 0:
        raise ValueError("luas tidak boleh negatif: " + str(m2))
    return m2 / 10000


try:
    luas_ke_hektar(-5)
except ValueError as e:
    print("Galat tertangkap:", e)

luas = 3
assert luas > 0, "luas harus positif"
print("lolos")

Penjelasannya:

  • raise ValueError("...") menghentikan fungsi dengan pesan buatan Anda yang jelas.
  • except ValueError as e menangkap galat itu, dan e berisi pesannya.
  • assert syarat, "pesan" memastikan syarat benar. Bila salah, skrip berhenti dengan AssertionError dan pesan Anda.
HASIL
Galat tertangkap: luas tidak boleh negatif: -5
lolos

Skrip yang dibedah: memeriksa masukan sebelum dipakai #

Tujuan. Membuka empat berkas kiriman lapangan satu per satu. Berkas yang bermasalah dilewati dengan pesan yang jelas, dan berkas yang baik tetap dibaca.

Bahan. Paket data paket-i4, folder Kiriman_Lapangan berisi tim_a.gpkg (baik), tim_e.gpkg (rusak), dan tim_f.gpkg (kolom Kondisi tidak ada). Berkas tim_x.gpkg memang tidak ada. Ganti dua baris awal DATA dan HASIL sesuai komputer Anda. Dijalankan di Konsol atau Editor QGIS.

Skrip utuh:

PYTHONUnduh berkas
# [SKRIP 4.1: Memeriksa masukan sebelum dipakai dan menangkap galat dengan try-except]
# Penulis: Badar Mubarok Yogaswara
import os
from qgis.core import QgsVectorLayer

DATA = "D:/Latihan/paket-i4"
HASIL = "D:/Latihan/hasil_i4"


def muat_vektor(jalur, nama, field_wajib=()):
    """Membuka layer vektor. Bila ada yang tidak beres, berhenti dengan pesan yang jelas."""
    if not os.path.exists(jalur):
        raise FileNotFoundError("berkas tidak ada: " + jalur)
    layer = QgsVectorLayer(jalur, nama, "ogr")
    if not layer.isValid():
        raise ValueError("berkas tidak bisa dibuka (mungkin rusak): " + os.path.basename(jalur))
    kurang = [f for f in field_wajib if layer.fields().indexOf(f) < 0]
    if kurang:
        raise ValueError("field wajib tidak ada: " + ", ".join(kurang))
    if layer.featureCount() == 0:
        raise ValueError("layer kosong, tidak ada fitur")
    return layer


# Empat berkas dicoba satu per satu. Satu gagal tidak boleh menghentikan yang lain.
kasus = [
    DATA + "/Kiriman_Lapangan/tim_a.gpkg",      # baik
    DATA + "/Kiriman_Lapangan/tim_x.gpkg",      # tidak ada
    DATA + "/Kiriman_Lapangan/tim_e.gpkg",      # rusak
    DATA + "/Kiriman_Lapangan/tim_f.gpkg",      # field Kondisi tidak ada
]
for jalur in kasus:
    nama = os.path.basename(jalur)
    try:
        layer = muat_vektor(jalur, nama, field_wajib=["Kondisi", "Tinggi_Phn"])
    except FileNotFoundError as e:
        print("[LEWATI] %s -> %s" % (nama, e))
    except ValueError as e:
        print("[LEWATI] %s -> %s" % (nama, e))
    else:
        # blok else: hanya jalan bila TIDAK ada galat
        print("[OK]     %s -> %d fitur" % (nama, layer.featureCount()))
    finally:
        # blok finally: selalu jalan, berhasil atau gagal
        print("         selesai memeriksa", nama)

# Galat hitungan yang sering terjadi: pembagian dengan nol
def persen(bagian, total):
    try:
        return 100 * bagian / total
    except ZeroDivisionError:
        return None          # tidak ada jawaban yang masuk akal

print("Persen 3 dari 12:", persen(3, 12))
print("Persen 3 dari 0:", persen(3, 0))

Penjelasan baris demi baris. Skrip ini panjang, jadi dibahas dalam empat blok. Nomor baris mengikuti skrip di atas.

Blok 1: pembuka (baris 1-7).

BarisTulisanArtinya
1-2# [SKRIP 4.1 ...Judul dan nama penulis.
3-4import os, from qgis.core import QgsVectorLayerBuka perkakas sistem dan perkakas pembuka layer vektor.
6-7DATA = ..., HASIL = ...Dua alamat di satu tempat (Bab 8). Ganti sesuai komputer Anda.

Blok 2: fungsi penjaga muat_vektor (baris 10-22). Fungsi ini memeriksa empat hal secara berurutan. Begitu satu pemeriksaan gagal, ia menimbulkan galat dengan pesan yang jelas, dan berhenti.

BarisTulisanArtinya
10def muat_vektor(jalur, nama, field_wajib=()):Buat fungsi dengan tiga bahan. Bahan ketiga boleh dikosongkan karena punya nilai bawaan: tuple kosong.
11"""Membuka layer ..."""Docstring: catatan penjelasan fungsi (Bab 7).
12-13if not os.path.exists(jalur): raise FileNotFoundError(...)Pemeriksaan 1: berkas harus ada. Bila tidak, timbulkan galat jenis FileNotFoundError dengan pesan buatan sendiri.
14-16layer = QgsVectorLayer(...) lalu if not layer.isValid(): raise ValueError(...)Pemeriksaan 2: berkas harus bisa dibuka. Berkas rusak menghasilkan layer tidak valid.
17-19kurang = [... for f in field_wajib if ... < 0] lalu raisePemeriksaan 3: semua kolom wajib harus ada. Comprehension (Bab 6) mengumpulkan kolom yang hilang. indexOf memberi -1 bila kolom tidak ada.
20-21if layer.featureCount() == 0: raise ...Pemeriksaan 4: layer tidak boleh kosong.
22return layerSemua lolos, kembalikan layernya.

Blok 3: perulangan dengan try (baris 25-45).

BarisTulisanArtinya
25-31kasus = [...]Daftar empat alamat berkas yang akan dicoba. Komentar menandai kondisi masing-masing.
32-33for jalur in kasus: lalu nama = os.path.basename(jalur)Ulangi tiap alamat. Ambil nama berkasnya saja.
34-35try: layer = muat_vektor(...)Coba buka berkas dengan fungsi penjaga. Kolom wajib: Kondisi dan Tinggi_Phn.
36-39except FileNotFoundError as e: dan except ValueError as e:Dua jenis galat ditangani. Tiap kali, cetak pesan [LEWATI] dan lanjut ke berkas berikutnya.
40-42else:Berjalan hanya bila tidak ada galat: cetak [OK] dan jumlah fitur.
43-45finally:Selalu berjalan: cetak "selesai memeriksa".

Blok 4: galat hitungan (baris 47-55). Fungsi persen memakai pola yang sama seperti contoh di awal bab. Ia mengembalikan None bila pembaginya nol.

Hasil. Jalankan dengan DATA menunjuk folder paket Anda. Hasil di komputer penulis (jalur pada baris [LEWATI] tim_x.gpkg mengikuti nilai DATA Anda):

HASIL
[OK]     tim_a.gpkg -> 6 fitur
         selesai memeriksa tim_a.gpkg
[LEWATI] tim_x.gpkg -> berkas tidak ada: D:/Latihan/paket-i4/Kiriman_Lapangan/tim_x.gpkg
         selesai memeriksa tim_x.gpkg
[LEWATI] tim_e.gpkg -> berkas tidak bisa dibuka (mungkin rusak): tim_e.gpkg
         selesai memeriksa tim_e.gpkg
[LEWATI] tim_f.gpkg -> field wajib tidak ada: Kondisi
         selesai memeriksa tim_f.gpkg
Persen 3 dari 12: 25.0
Persen 3 dari 0: None

Satu berkas baik dan tiga berkas bermasalah, tetapi keempatnya diperiksa. Itulah tujuan try/except di dalam perulangan.

Bagian yang boleh diubah.

  • Baris 6, lokasi data. Ganti dengan lokasi paket-i4 di komputer Anda.
  • Baris 26-31, daftar berkas. Ganti dengan berkas Anda sendiri.
  • Baris 35, kolom wajib. Ganti nama kolom sesuai data Anda: field_wajib=["KODE", "LUAS"].
  • Isi pesan di print dan raise. Ganti dengan kalimat yang paling jelas bagi rekan kerja Anda.

Jebakan.

  • Urutan except. Python memakai except pertama yang cocok. Tulis galat yang spesifik di atas, dan yang umum di bawah.
  • except kosong. Menulis except: tanpa jenis menangkap semuanya, termasuk salah ketik Anda sendiri, dan menyembunyikan masalah. Selalu sebut jenis galatnya.
  • Jalur contoh. Bila DATA belum diganti, semua berkas akan [LEWATI] dengan pesan "berkas tidak ada". Itu bukan kerusakan data.
  • Menyembunyikan terlalu banyak. Menangkap galat lalu diam saja membuat masalah baru tak terlihat. Selalu cetak atau catat sesuatu.

Latihan variasi. Tambahkan satu berkas ke daftar kasus, yaitu tim_c.gpkg, lalu jalankan lagi. Apa hasilnya?

Jawabannya bergantung pada isi tim_c.gpkg. Pola yang Anda pelajari memberi tahu Anda cara mencari tahu: baca baris [OK] atau [LEWATI]. Bila [LEWATI], pesan setelah panah menyebut alasannya.

Cek paham #

  1. Pada pesan galat, baris mana yang dibaca pertama?
  2. Apa beda else dan finally pada blok try?
  3. Mengapa except: tanpa jenis galat tidak dianjurkan?

Jawaban.

  1. Baris paling bawah, yang menyebut jenis dan alasan galat.
  2. else berjalan hanya bila tidak ada galat. finally selalu berjalan.
  3. Karena menangkap semua galat, termasuk kesalahan tulis Anda sendiri, sehingga masalah tersembunyi.

Kesalahan umum #

  • Panik melihat tulisan merah panjang. Perbaikan: baca baris paling bawah dulu, lalu naik.
  • Menangkap galat tanpa mencetak apa pun. Perbaikan: selalu beri pesan, supaya Anda tahu apa yang dilewati.
  • Membungkus seluruh skrip dalam satu try. Perbaikan: bungkus hanya bagian yang memang bisa gagal, misalnya membuka berkas.

Ringkasan dan latihan #

  • Baca galat dari bawah ke atas: jenis dan alasan, baris penyebab, nama berkas dan nomor baris.
  • try/except/else/finally menangani galat tanpa menghentikan skrip.
  • raise menimbulkan galat dengan pesan sendiri. assert memastikan sebuah syarat.

Latihan. Tulis fungsi rata_rata(daftar) yang mengembalikan None bila daftar kosong, dan rata-ratanya bila tidak. Uji dengan daftar kosong dan daftar berisi tiga angka.

Tabel perbandingan: empat bagian blok try #

BagianKapan berjalanKegunaan
trySelalu dicoba lebih duluPekerjaan yang bisa gagal
exceptHanya bila galat itu munculMenangani galat dan mencetak pesan
elseHanya bila tidak ada galatJalur sukses
finallySelaluMerapikan, menutup, atau mencatat