Pernyataan match membandingkan sebuah nilai terhadap pola (pattern), mengikat bagian-bagian yang cocok ke nama baru, lalu menjalankan cabang yang sesuai. Kemampuan destrukturisasi data bersarang inilah yang membedakannya dari pencabangan if biasa, dan tidak ditemukan pada perbandingan kesetaraan. Topik ini membahas pola sequence dan mapping bersarang, guard yang menambah syarat, pola OR, serta penerapannya pada penguraian baris log.
Membongkar data bersarang#
Pola sequence membongkar list dan tuple menurut posisi, sedangkan pola mapping membongkar dictionary menurut kunci. Keduanya dapat disarangkan, dan simbol * atau ** menampung sisanya. Nama yang dituliskan pada pola otomatis menjadi variabel yang berisi bagian data yang cocok.
data = {"aksi": "kirim", "muatan": {"penerima": "Budi", "jumlah": 3, "label": ["fragil", "dingin"]}}
match data:
case {"aksi": "kirim", "muatan": {"penerima": penerima, "jumlah": jumlah, **sisanya}}:
print("mengirim ke", penerima, "sebanyak", jumlah)
print("kunci lain:", sorted(sisanya))
case _:
print("format tidak dikenal")mengirim ke Budi sebanyak 3
kunci lain: ['label']Pola di atas menuntut kunci aksi bernilai literal "kirim" dan kunci muatan berbentuk dictionary dengan dua kunci tertentu. Nama penerima dan jumlah berperan sebagai pola tangkap yang mengikat nilainya, sedangkan **sisanya mengumpulkan kunci yang tidak disebutkan. Pola sequence dengan * bekerja dengan cara yang sama untuk list.
riwayat = ["mulai", 3, 5, 7, "selesai"]
match riwayat:
case ["mulai", *tengah, "selesai"]:
print(tengah)
case _:
print("tidak cocok")[3, 5, 7]Pola ini menuntut elemen pertama bernilai "mulai" dan elemen terakhir bernilai "selesai", sedangkan seluruh elemen di antaranya dikumpulkan ke tengah berapa pun panjangnya. Kombinasi pembatas tetap di ujung dan pengumpul di tengah cocok untuk data berformat tetap dengan isi yang bervariasi, seperti rekam jejak eksekusi.
Guard dan pola OR#
Pola hanya mengenali bentuk data, sedangkan syarat nilai yang lebih bebas ditempatkan pada guard, yaitu klausa if yang mengikuti pola. Cabang hanya dipilih bila polanya cocok dan guardnya benar, dan bila guardnya salah, pencocokan berlanjut ke cabang berikutnya. Pola OR memperluas pilihan dengan memperbolehkan beberapa pola berbagi satu cabang melalui tanda |.
for nilai in (78, 92, 55):
match nilai:
case n if n >= 85:
print(n, "-> A")
case n if n >= 70:
print(n, "-> B")
case _:
print(n, "-> C")78 -> B
92 -> A
55 -> CNilai 78 tidak lolos guard pertama yang menuntut 85 ke atas, sehingga pencocokan turun ke guard berikutnya dan berakhir di kategori B. Urutan cabang dengan demikian menjadi bagian dari logika, sebagaimana if/elif. Pada pola OR, urutan justru tidak berpengaruh karena semua pilihannya setara.
Guard bukan tempat penanganan exception. Ekspresi yang menimbulkan error di dalam guard meneruskan error itu ke luar pernyataan match, alih-alih dianggap pencocokan yang gagal. Karena itu, pemanggilan fungsi yang bisa gagal sebaiknya diuji terlebih dahulu sebelum dipakai sebagai guard.
for perintah in ["maju", "stop", "keluar", "unduh"]:
match perintah:
case "stop" | "keluar":
print(perintah, "-> penghentian")
case "maju" | "mundur":
print(perintah, "-> pergerakan")
case _:
print(perintah, "-> tidak dikenal")maju -> pergerakan
stop -> penghentian
keluar -> penghentian
unduh -> tidak dikenalSetiap literal pada kelompok pertama memilih cabang penghentian, sehingga literal dalam kelompok itu tidak perlu ditulis dalam case tersendiri. Guard dan pola OR dapat digabung pada satu cabang, misalnya case [x, y] if x > y, yang menuntut polanya cocok dan syaratnya terpenuhi sekaligus.
Program utuh: pengurai baris log#
Contoh berikut menerapkan keseluruhan fitur pada pekerjaan yang umum, yaitu mengurai baris log berformat terbatas. Setiap baris dipecah menjadi kata, lalu dicocokkan dengan pola sequence yang menggambarkan formatnya, lengkap dengan guard untuk validasi angka dan pola tangkap * untuk isi bebas.
baris_log = [
"SET suhu 28",
"NAIK suhu 4",
"HAPUS suhu",
"CATATAN gudang dibuka pukul tujuh",
"SET tekanan puluhan",
"RESET",
]
for baris in baris_log:
bagian = baris.split()
match bagian:
case ["SET", kunci, nilai] if nilai.isdigit():
print("menetapkan", kunci, "=", int(nilai))
case ["SET", kunci, nilai]:
print("nilai", nilai, "bukan angka, kunci", kunci, "diabaikan")
case ["NAIK", kunci, nilai]:
print("menaikkan", kunci, "sebanyak", int(nilai))
case ["HAPUS", kunci]:
print("menghapus", kunci)
case ["CATATAN", *isi]:
print("catatan:", " ".join(isi))
case []:
print("baris kosong dilewati")
case _:
print("baris tidak dikenal:", baris)menetapkan suhu = 28
menaikkan suhu sebanyak 4
menghapus suhu
catatan: gudang dibuka pukul tujuh
nilai puluhan bukan angka, kunci tekanan diabaikan
baris tidak dikenal: RESETSimpan berkas di atas di direktori kerja, lalu jalankan dengan perintah berikut.
# menjalankan program pengurai baris log
python3 urai_log.pymenetapkan suhu = 28
menaikkan suhu sebanyak 4
menghapus suhu
catatan: gudang dibuka pukul tujuh
nilai puluhan bukan angka, kunci tekanan diabaikan
baris tidak dikenal: RESETSetiap baris log jatuh ke cabang yang tepat tanpa rantai if bertingkat. Baris SET suhu 28 lolos guard isdigit(), sedangkan SET tekanan puluhan berbentuk sama tetapi gagal guard, sehingga jatuh ke cabang SET kedua yang menangani nilai tidak valid. Baris CATATAN memakai pola tangkap *isi agar isi bebasnya tidak perlu dihitung, dan baris RESET ditampung cabang case _ sebagai penampung segala bentuk yang tersisa.
Kesalahan umum#
Kesalahan pertama adalah memperlakukan nama pada pola sebagai perbandingan terhadap variabel yang sudah ada. Pola case status merupakan pola tangkap yang mengikat nilai apa pun, sehingga cabangnya selalu terpilih dan variabel luar tertimpa.
status = "berhenti"
for perintah in ["jalan", "berhenti"]:
match perintah:
case status:
print("pola tangkap menangkap", status)
class Sinyal:
BERHENTI = "berhenti"
for perintah in ["jalan", "berhenti"]:
match perintah:
case Sinyal.BERHENTI:
print("pola nilai mengenali", perintah)
case _:
print("bukan perintah berhenti:", perintah)pola tangkap menangkap jalan
pola tangkap menangkap berhenti
bukan perintah berhenti: jalan
pola nilai mengenali berhentiDua baris pertama memperlihatkan bahwa case status menangkap "jalan" yang seharusnya tidak cocok, sekaligus menimpa variabel status luar. Perbaikannya pada perulangan kedua memakai pola nilai bertitik Sinyal.BERHENTI, yang dibandingkan dengan nilai perintah dan tidak mengikat apa pun. Aturan praktisnya, nama tunggal selalu berarti tangkap, sedangkan perbandingan membutuhkan literal atau nama bertitik.
Kesalahan kedua adalah lupa menyediakan cabang penampung. Apabila tidak ada pola yang cocok dan case _ tidak tersedia, pernyataan match berlalu tanpa menjalankan apa pun, dan program berlanjut tanpa keluaran maupun error. Keheningan semacam ini sulit dilacak pada data yang bentuknya bervariasi. Karena itu, cabang case _ yang mencatat nilai tak dikenal layak disertakan pada setiap match yang menerima masukan luar, seperti pada program pengurai log di atas.
Ringkasan#
- Pola sequence dan mapping membongkar list serta dictionary menurut posisi dan kunci, dengan
*dan**menampung sisanya. - Guard
ifmenambah syarat nilai pada satu cabang, sedangkan pola OR melalui|menghimpun beberapa pola pada cabang yang sama. - Nama tunggal pada pola selalu berarti pola tangkap, sehingga perbandingan dengan nilai yang sudah ada membutuhkan literal atau nama bertitik.
matchtanpa cabang yang cocok tidak menjalankan apa pun, sehingga cabangcase _menjadi penampung yang aman untuk masukan tak dikenal.
Referensi#
- PEP 634 — Structural Pattern Matching — Python Software Foundation — Spesifikasi lengkap pola sequence, mapping, guard, dan pola OR.
- match Statements — Python Software Foundation — Bagian tutorial resmi tentang pernyataan match dan pola dasarnya.
- The match statement — Python Software Foundation — Referensi bahasa untuk semantik pencocokan dan aturan pengikatannya.