Seleksi Kondisi #
Seleksi kondisi adalah mekanisme yang memungkinkan program mengambil jalur eksekusi yang berbeda berdasarkan kondisi tertentu. Di Python, ada dua konstruksi utama untuk ini: if/elif/else yang sudah ada sejak awal, dan match/case yang diperkenalkan di Python 3.10 sebagai bentuk structural pattern matching yang jauh lebih kaya dari sekadar switch di bahasa lain. Yang sama pentingnya dengan memahami sintaksnya adalah memahami bagaimana menyusun kondisi agar kode tetap mudah dibaca — karena percabangan yang dalam dan berlapis adalah salah satu penyebab utama kode yang sulit dipelihara.
if, elif, dan else
#
Bentuk dasar seleksi kondisi di Python. Titik dua : mengakhiri baris kondisi, dan blok kode di bawahnya diindentasi 4 spasi.
# Struktur dasar
nilai = 78
if nilai >= 90:
print("A")
elif nilai >= 80:
print("B")
elif nilai >= 70:
print("C")
elif nilai >= 60:
print("D")
else:
print("E")
# → C
Beberapa hal penting tentang perilaku if/elif/else:
# elif dievaluasi HANYA jika semua kondisi sebelumnya False
# Begitu satu kondisi True, sisanya dilewati seluruhnya
x = 15
if x > 10:
print("lebih dari 10") # ← ini dieksekusi
elif x > 5:
print("lebih dari 5") # ← ini TIDAK dievaluasi meski 15 > 5
else:
print("5 atau kurang") # ← ini juga tidak
# else bersifat opsional
umur = 20
if umur >= 18:
print("boleh masuk")
# tidak perlu else jika tidak ada aksi untuk kasus lain
Kondisi Truthy dan Falsy dalam if
#
Python mengevaluasi ekspresi apapun sebagai kondisi — tidak harus boolean eksplisit. Ini memungkinkan idiom yang lebih ringkas tapi perlu dipahami dengan baik.
# Nilai yang dianggap False (falsy):
# False, 0, 0.0, "", [], {}, set(), tuple(), None
# Nilai yang dianggap True (truthy):
# semua yang lain
nama = ""
data = []
pengguna = None
# ANTI-PATTERN: perbandingan eksplisit yang tidak perlu
if nama == "":
print("nama kosong")
if data == []:
print("list kosong")
if pengguna == None: # seharusnya 'is None'
print("tidak ada pengguna")
# BENAR: manfaatkan truthy/falsy secara langsung
if not nama:
print("nama kosong")
if not data:
print("list kosong")
if pengguna is None: # None selalu dengan 'is'
print("tidak ada pengguna")
# Kasus yang sering mengejutkan — angka 0 adalah falsy
stok = 0
if not stok:
print("stok habis") # → stok habis (benar secara logika)
# Tapi hati-hati jika 0 adalah nilai valid yang berbeda dari "tidak ada"
skor = 0
if not skor:
print("skor belum diisi") # ← SALAH! 0 adalah skor valid
# BENAR: bedakan antara "nilai 0" dan "tidak ada nilai"
skor = 0
if skor is None:
print("skor belum diisi")
else:
print(f"skor: {skor}") # → skor: 0
Nested if dan Cara Menghindarinya
#
Kondisi bersarang (nested) sering muncul secara alami, tapi terlalu dalam akan membuat kode sulit dibaca dan dipelihara. Ada beberapa teknik untuk meratakannya.
# ANTI-PATTERN: nested if yang dalam — "pyramid of doom"
def proses_transaksi(pengguna, jumlah):
if pengguna is not None:
if pengguna.aktif:
if jumlah > 0:
if jumlah <= pengguna.saldo:
if pengguna.batas_harian >= jumlah:
lakukan_transfer(pengguna, jumlah)
return True
else:
return False
else:
return False
else:
return False
else:
return False
else:
return False
Teknik 1: Guard Clause (Early Return) #
Balik kondisi dan kembalikan nilai lebih awal untuk kondisi yang tidak terpenuhi. Kode “jalur bahagia” tetap di indentasi paling kiri.
# BENAR: guard clause membalik setiap kondisi dan return lebih awal
def proses_transaksi(pengguna, jumlah):
if pengguna is None:
return False
if not pengguna.aktif:
return False
if jumlah <= 0:
return False
if jumlah > pengguna.saldo:
return False
if jumlah > pengguna.batas_harian:
return False
# jalur bahagia — semua validasi lulus
lakukan_transfer(pengguna, jumlah)
return True
Teknik 2: Gabungkan Kondisi dengan and
#
# ANTI-PATTERN: nested if untuk kondisi yang sebenarnya bersamaan
def cek_akses(pengguna, fitur):
if pengguna.login:
if pengguna.aktif:
if fitur in pengguna.izin:
return True
return False
# BENAR: gabungkan dengan and
def cek_akses(pengguna, fitur):
if pengguna.login and pengguna.aktif and fitur in pengguna.izin:
return True
return False
# Atau lebih ringkas lagi
def cek_akses(pengguna, fitur):
return pengguna.login and pengguna.aktif and fitur in pengguna.izin
Teknik 3: Ekstrak ke Fungsi Terpisah #
# ANTI-PATTERN: logika validasi panjang di dalam if
def simpan_produk(data):
if (data.get("nama") and
len(data["nama"]) >= 3 and
data.get("harga") and
data["harga"] > 0 and
data.get("stok") is not None and
data["stok"] >= 0):
# simpan ke database
db.simpan(data)
# BENAR: ekstrak validasi ke fungsi tersendiri
def produk_valid(data):
"""Kembalikan True jika data produk memenuhi semua syarat."""
if not data.get("nama") or len(data["nama"]) < 3:
return False
if not data.get("harga") or data["harga"] <= 0:
return False
if data.get("stok") is None or data["stok"] < 0:
return False
return True
def simpan_produk(data):
if produk_valid(data):
db.simpan(data)
Conditional Expression (Ternary) #
Python menyediakan ekspresi kondisional satu baris — sering disebut ternary expression. Berguna untuk assignment sederhana, tapi jangan dipaksakan untuk kondisi yang kompleks.
# Sintaks: nilai_jika_true if kondisi else nilai_jika_false
x = 10
label = "positif" if x > 0 else "nol atau negatif"
print(label) # → positif
# Penggunaan umum
umur = 20
status = "dewasa" if umur >= 18 else "belum dewasa"
diskon = 0.10 if total_belanja >= 500_000 else 0
harga_akhir = harga * (1 - diskon)
# Bisa digunakan langsung dalam ekspresi lain
print(f"Kamu {'boleh' if umur >= 18 else 'belum boleh'} masuk.")
# ANTI-PATTERN: ternary bersarang — sangat sulit dibaca
hasil = "A" if nilai >= 90 else "B" if nilai >= 80 else "C" if nilai >= 70 else "D"
# BENAR: gunakan if/elif/else biasa untuk kondisi multi-cabang
if nilai >= 90:
hasil = "A"
elif nilai >= 80:
hasil = "B"
elif nilai >= 70:
hasil = "C"
else:
hasil = "D"
Kondisi dengan Koleksi #
Beberapa pola kondisi yang sering digunakan bersama koleksi data:
# Cek keberadaan nilai dalam list/set/dict
peran_admin = {"admin", "superadmin", "moderator"}
peran_pengguna = "admin"
# ANTI-PATTERN: beberapa or yang bertele-tele
if peran_pengguna == "admin" or peran_pengguna == "superadmin" or peran_pengguna == "moderator":
beri_akses_panel()
# BENAR: gunakan 'in' dengan set
if peran_pengguna in peran_admin:
beri_akses_panel()
# Kondisi berdasarkan panjang koleksi
pesan_list = []
# ANTI-PATTERN
if len(pesan_list) == 0:
print("tidak ada pesan")
if len(pesan_list) > 0:
tampilkan_pesan()
# BENAR: manfaatkan truthy/falsy koleksi
if not pesan_list:
print("tidak ada pesan")
if pesan_list:
tampilkan_pesan()
# Dispatch table — alternatif if/elif panjang menggunakan dict
def aksi_tambah(): print("menambah item")
def aksi_hapus(): print("menghapus item")
def aksi_ubah(): print("mengubah item")
def aksi_lihat(): print("melihat item")
# ANTI-PATTERN: if/elif panjang untuk routing aksi
def jalankan_aksi(perintah):
if perintah == "tambah":
aksi_tambah()
elif perintah == "hapus":
aksi_hapus()
elif perintah == "ubah":
aksi_ubah()
elif perintah == "lihat":
aksi_lihat()
else:
print(f"perintah tidak dikenal: {perintah}")
# BENAR: dispatch table dengan dict
AKSI_MAP = {
"tambah": aksi_tambah,
"hapus": aksi_hapus,
"ubah": aksi_ubah,
"lihat": aksi_lihat,
}
def jalankan_aksi(perintah):
aksi = AKSI_MAP.get(perintah)
if aksi:
aksi()
else:
print(f"perintah tidak dikenal: {perintah}")
match/case — Structural Pattern Matching
#
Diperkenalkan di Python 3.10, match/case bukan sekadar switch. Ia bisa mencocokkan struktur data — nilai literal, tuple, list, dict, tipe objek — sekaligus mengekstrak nilai dari dalamnya.
Pencocokan Nilai Literal #
def nama_hari(nomor):
match nomor:
case 1:
return "Senin"
case 2:
return "Selasa"
case 3:
return "Rabu"
case 4:
return "Kamis"
case 5:
return "Jumat"
case 6:
return "Sabtu"
case 7:
return "Minggu"
case _: # wildcard — cocok dengan apa saja (seperti default)
return "Tidak valid"
print(nama_hari(3)) # → Rabu
print(nama_hari(9)) # → Tidak valid
Pencocokan dengan | (OR Pattern)
#
def kategori_hari(nomor):
match nomor:
case 6 | 7:
return "Akhir pekan"
case 1 | 2 | 3 | 4 | 5:
return "Hari kerja"
case _:
return "Tidak valid"
print(kategori_hari(6)) # → Akhir pekan
print(kategori_hari(3)) # → Hari kerja
Pencocokan Sequence (Tuple/List) #
Ini adalah keunggulan utama match/case — bisa mencocokkan struktur dan sekaligus mengekstrak nilainya ke variabel:
def describe_koordinat(titik):
match titik:
case (0, 0):
return "Titik asal"
case (0, y): # x=0, y bebas → ditangkap ke variabel y
return f"Di sumbu Y: y={y}"
case (x, 0): # y=0, x bebas → ditangkap ke variabel x
return f"Di sumbu X: x={x}"
case (x, y) if x == y: # guard condition
return f"Di garis diagonal: ({x}, {y})"
case (x, y): # semua titik lain
return f"Titik biasa: ({x}, {y})"
case _:
return "Bukan koordinat 2D"
print(describe_koordinat((0, 0))) # → Titik asal
print(describe_koordinat((0, 5))) # → Di sumbu Y: y=5
print(describe_koordinat((3, 0))) # → Di sumbu X: x=3
print(describe_koordinat((4, 4))) # → Di garis diagonal: (4, 4)
print(describe_koordinat((3, 7))) # → Titik biasa: (3, 7)
Pencocokan Dictionary (Mapping Pattern) #
def proses_event(event):
match event:
case {"tipe": "klik", "tombol": "kiri", "x": x, "y": y}:
print(f"Klik kiri di ({x}, {y})")
case {"tipe": "klik", "tombol": "kanan", "x": x, "y": y}:
print(f"Klik kanan di ({x}, {y})")
case {"tipe": "keyboard", "kunci": kunci} if kunci.startswith("F"):
print(f"Tombol fungsi: {kunci}")
case {"tipe": "keyboard", "kunci": kunci}:
print(f"Tombol: {kunci}")
case _:
print("Event tidak dikenal")
proses_event({"tipe": "klik", "tombol": "kiri", "x": 100, "y": 200})
# → Klik kiri di (100, 200)
proses_event({"tipe": "keyboard", "kunci": "F5"})
# → Tombol fungsi: F5
Mapping pattern dimatch/casebersifat partial — artinya dict yang dicocokkan boleh memiliki kunci tambahan selain yang disebutkan dalam pattern dan tetap cocok. Jika ingin cocokkan secara persis, tambahkan**_untuk menangkap sisa kunci.
Pencocokan Tipe Objek (Class Pattern) #
from dataclasses import dataclass
@dataclass
class Lingkaran:
jari_jari: float
@dataclass
class Persegi:
sisi: float
@dataclass
class Segitiga:
alas: float
tinggi: float
def hitung_luas(bentuk):
match bentuk:
case Lingkaran(jari_jari=r):
return 3.14159 * r ** 2
case Persegi(sisi=s):
return s ** 2
case Segitiga(alas=a, tinggi=t):
return 0.5 * a * t
case _:
raise ValueError(f"Bentuk tidak dikenal: {bentuk}")
print(hitung_luas(Lingkaran(7))) # → 153.938...
print(hitung_luas(Persegi(5))) # → 25.0
print(hitung_luas(Segitiga(6, 4))) # → 12.0
Kapan Gunakan match/case vs if/elif
#
Gunakan if/elif jika:
✓ kondisi melibatkan perbandingan numerik (>, <, >=)
✓ kondisi melibatkan banyak variabel berbeda
✓ kondisi mudah digabungkan dengan and/or
✓ kamu perlu kompatibilitas dengan Python < 3.10
Gunakan match/case jika:
✓ mencocokkan struktur data (tuple, dict, objek)
✓ mengekstrak nilai dari struktur sekaligus mencocokkan
✓ kondisi bergantung pada tipe objek
✓ menggantikan if/elif panjang yang membandingkan satu variabel
Ringkasan #
- Guard clause (early return) adalah cara terbaik menghindari nested if yang dalam — balik kondisi dan return lebih awal, biarkan jalur bahagia berada di indentasi paling kiri.
- Hindari membandingkan dengan
== True,== False, atau== []— cukup evaluasi ekspresi langsung, atau gunakannotuntuk kasus falsy.is Nonebukan== None— selalu gunakanis/is notuntuk membandingkan denganNone.- Dispatch table (dict of functions) lebih bersih dari if/elif panjang untuk routing berdasarkan nilai string atau enum.
- Ternary expression bagus untuk kondisi sederhana dua cabang, tapi jangan bersarang — gunakan if/elif biasa jika lebih dari dua cabang.
match/casebukan sekadar switch — ia bisa mencocokkan struktur data dan mengekstrak nilai ke variabel sekaligus, tersedia sejak Python 3.10.- Guard condition dalam
match/casemenggunakanifsetelah pattern — memungkinkan kondisi tambahan di luar pencocokan struktur.- Gabungkan kondisi terkait dengan
andalih-alih membuat nested if — lebih datar, lebih mudah dibaca.