BAB 24: *args dan **kwargs
Buat fungsi yang bisa menerima argumen dalam jumlah tak terbatas menggunakan *args untuk positional dan **kwargs untuk keyword argument.
Di bab sebelumnya, kita sudah membangun fungsi buat_laporan yang menerima dua parameter wajib dan dua parameter opsional. Fungsi itu fleksibel — tapi fleksibilitasnya masih dalam batas yang kita tentukan dari awal. Bagaimana kalau kebutuhan berubah dan kita tidak tahu dari awal ada berapa argumen yang akan dilempar?
Bayangkan kita ingin menambah fitur ke sistem kuis: sebuah fungsi yang bisa merekap nilai dari berapapun jumlah peserta — entah tiga orang, sepuluh orang, atau seratus orang — tanpa harus mendefinisikan parameter satu per satu. Inilah problem yang diselesaikan oleh *args dan **kwargs.
Apa Itu *args
*args adalah cara Python untuk mengatakan: “terima semua argumen positional yang tersisa, kumpulkan jadi satu tuple.” Tanda bintang (*) di depan nama parameter adalah sintaks yang memberitahu Python untuk melakukan pengumpulan ini.
Perhatikan bedanya dengan fungsi biasa:
Karena *args menghasilkan tuple, semua operasi yang bisa dilakukan pada tuple — iterasi, indexing, len() — bisa dilakukan pada *args.
Fungsi dengan *args
Mari kita tambahkan fitur rekap ke sistem kuis. Fungsi rekap_nilai ini harus bisa menerima nilai dari berapapun peserta:
# kuis.py — rekap nilai dengan *args
def rekap_nilai(*daftar_nilai: float) -> dict:
if not daftar_nilai:
return {"total": 0, "rata_rata": 0, "tertinggi": 0, "terendah": 0}
return {
"total": len(daftar_nilai),
"rata_rata": sum(daftar_nilai) / len(daftar_nilai),
"tertinggi": max(daftar_nilai),
"terendah": min(daftar_nilai),
}
# panggil dengan tiga nilai
hasil_kelas_a = rekap_nilai(88.2, 77.2, 51.0)
# panggil dengan lima nilai — fungsi yang sama, beda jumlah argumen
hasil_kelas_b = rekap_nilai(92.0, 85.5, 78.3, 66.0, 91.0)
print("Kelas A:")
for kunci, nilai in hasil_kelas_a.items():
print(f" {kunci}: {nilai:.1f}" if isinstance(nilai, float) else f" {kunci}: {nilai}")
print("\nKelas B:")
for kunci, nilai in hasil_kelas_b.items():
print(f" {kunci}: {nilai:.1f}" if isinstance(nilai, float) else f" {kunci}: {nilai}")
Kelas A:
total: 3
rata_rata: 72.1
tertinggi: 88.2
terendah: 51.0
Kelas B:
total: 5
rata_rata: 82.6
tertinggi: 92.0
terendah: 66.0
Satu fungsi, dua panggilan dengan jumlah argumen berbeda — keduanya berjalan sempurna. Di dalam fungsi, daftar_nilai adalah tuple biasa yang bisa ditelusuri dengan for, dihitung dengan len(), atau diolah dengan sum().
Kombinasi dengan Parameter Biasa
*args bisa dikombinasikan dengan parameter biasa, dengan satu syarat: parameter biasa harus dideklarasikan sebelum *args. Python akan mengisi parameter biasa terlebih dahulu, lalu semua sisa argumen masuk ke *args.
# kuis.py — kombinasi parameter biasa dan *args
def cetak_rekap_kelas(nama_kelas: str, *daftar_nilai: float):
statistik = rekap_nilai(*daftar_nilai) # teruskan ke fungsi rekap
print(f"=== Rekap {nama_kelas} ===")
print(f"Peserta : {statistik['total']} orang")
print(f"Rata-rata: {statistik['rata_rata']:.1f}")
print(f"Tertinggi: {statistik['tertinggi']:.1f}")
print(f"Terendah : {statistik['terendah']:.1f}")
cetak_rekap_kelas("Kelas A", 88.2, 77.2, 51.0)
cetak_rekap_kelas("Kelas B", 92.0, 85.5, 78.3, 66.0, 91.0)
=== Rekap Kelas A ===
Peserta : 3 orang
Rata-rata: 72.1
Tertinggi: 88.2
Terendah : 51.0
=== Rekap Kelas B ===
Peserta : 5 orang
Rata-rata: 82.6
Tertinggi: 92.0
Terendah : 66.0
Perhatikan rekap_nilai(*daftar_nilai) — tanda bintang di sini berfungsi sebaliknya: ia membongkar tuple daftar_nilai menjadi argumen-argumen individual saat memanggil fungsi. Ini disebut unpacking, dan sangat berguna saat meneruskan *args ke fungsi lain.
Nama args adalah konvensi, bukan keharusan. *daftar_nilai, *nilai_peserta, atau nama apapun yang bermakna lebih baik daripada *args yang generik. Gunakan nama yang mencerminkan isi datanya.
Apa Itu **kwargs
**kwargs melakukan hal yang mirip dengan *args, tapi untuk keyword argument. Semua keyword argument yang tidak tertangkap oleh parameter lain akan dikumpulkan ke dalam satu dictionary.
Mari tambahkan fitur metadata ke sistem kuis — fungsi yang bisa menerima informasi tambahan apapun tentang sebuah sesi kuis, tanpa kita perlu tahu dari awal informasi apa saja yang akan diberikan:
# kuis.py — metadata sesi dengan **kwargs
def catat_sesi_kuis(id_sesi: str, **metadata):
print(f"Sesi: {id_sesi}")
if metadata:
print("Informasi tambahan:")
for kunci, nilai in metadata.items():
print(f" - {kunci}: {nilai}")
else:
print(" (tidak ada metadata)")
catat_sesi_kuis("SESI-001")
print()
catat_sesi_kuis(
"SESI-002",
pengawas="Bu Sari",
ruangan="Lab 3",
durasi_menit=90,
catatan="Ujian susulan"
)
Sesi: SESI-001
(tidak ada metadata)
Sesi: SESI-002
Informasi tambahan:
- pengawas: Bu Sari
- ruangan: Lab 3
- durasi_menit: 90
- catatan: Ujian susulan
**metadata menangkap semua keyword argument — pengawas, ruangan, durasi_menit, catatan — dan menyimpannya sebagai dictionary. Fungsi ini tidak perlu tahu sebelumnya field apa saja yang akan diberikan.
Urutan Kombinasi Lengkap
Python mendukung kombinasi semua jenis parameter dalam satu fungsi. Urutannya harus mengikuti aturan yang ketat:
Contoh nyata yang menggabungkan semuanya dalam konteks sistem kuis:
# kuis.py — fungsi dengan kombinasi lengkap
def buat_laporan_sesi(
nama_kelas: str,
*daftar_nilai: float,
format_output: str = "ringkas",
**metadata
) -> str:
statistik = rekap_nilai(*daftar_nilai)
baris = [f"Laporan Kelas: {nama_kelas}"]
if format_output == "detail":
baris.append(f" Peserta : {statistik['total']} orang")
baris.append(f" Rata-rata: {statistik['rata_rata']:.1f}")
baris.append(f" Tertinggi: {statistik['tertinggi']:.1f}")
baris.append(f" Terendah : {statistik['terendah']:.1f}")
if metadata:
baris.append(" Metadata :")
for k, v in metadata.items():
baris.append(f" {k}: {v}")
else:
baris.append(
f" {statistik['total']} peserta | "
f"rata-rata {statistik['rata_rata']:.1f}"
)
return "\n".join(baris)
# ringkas — hanya *args
print(buat_laporan_sesi("Kelas A", 88.2, 77.2, 51.0))
print()
# detail — *args + keyword_only + **kwargs
print(buat_laporan_sesi(
"Kelas B",
92.0, 85.5, 78.3, 66.0, 91.0,
format_output="detail",
pengawas="Bu Sari",
ruangan="Lab 3"
))
Laporan Kelas: Kelas A
3 peserta | rata-rata 72.1
Laporan Kelas: Kelas B
Peserta : 5 orang
Rata-rata: 82.6
Tertinggi: 92.0
Terendah : 66.0
Metadata :
pengawas: Bu Sari
ruangan: Lab 3
Parameter format_output berada setelah *daftar_nilai, yang menjadikannya keyword-only parameter — ia hanya bisa diisi menggunakan nama parameter secara eksplisit, tidak bisa secara positional. Python secara otomatis memberlakukan aturan ini.
Jika kamu meletakkan **kwargs sebelum *args, atau menaruh parameter biasa setelah *args tanpa menyebutnya sebagai keyword, Python akan menolak definisi fungsi itu dengan SyntaxError. Periksa urutan parameter dengan teliti.
Unpacking saat Memanggil Fungsi
Tanda * dan ** juga bisa digunakan saat memanggil fungsi — bukan hanya saat mendefinisikannya. Ini berguna ketika data yang ingin dilempar sudah tersimpan dalam list atau dictionary.
# kuis.py — unpacking list dan dict saat pemanggilan
nilai_kelas_c = [75.0, 83.5, 69.0, 91.5]
info_sesi = {"pengawas": "Pak Dodi", "ruangan": "Aula"}
# tanpa unpacking — harus tulis satu per satu
# cetak_rekap_kelas("Kelas C", 75.0, 83.5, 69.0, 91.5)
# dengan unpacking — langsung dari list
cetak_rekap_kelas("Kelas C", *nilai_kelas_c)
print()
# gabung positional + dict unpacking
catat_sesi_kuis("SESI-003", **info_sesi)
=== Rekap Kelas C ===
Peserta : 4 orang
Rata-rata: 79.8
Tertinggi: 91.5
Terendah : 69.0
Sesi: SESI-003
Informasi tambahan:
- pengawas: Pak Dodi
- ruangan: Aula
Unpacking *nilai_kelas_c membongkar list menjadi argumen individual sebelum dilempar ke fungsi. Begitu pula **info_sesi yang membongkar dictionary menjadi keyword argument. Ini cara yang sangat umum digunakan saat bekerja dengan data yang sudah tersimpan dalam koleksi.
Latihan
Tiga latihan untuk mengasah pemahaman tentang parameter variadic ini:
-
Tulis fungsi
bandingkan_kelas(*pasangan_kelas)yang menerima pasangan tuple(nama_kelas, [daftar_nilai]). Untuk setiap kelas, hitung rata-ratanya dan cetak perbandingan: kelas mana yang punya rata-rata tertinggi. Gunakanrekap_nilai()yang sudah kita buat. -
Tulis fungsi
format_laporan(judul: str, **bagian)yang menerima beberapa bagian laporan sebagai keyword argument. Setiap key adalah nama bagian, setiap value adalah isinya. Cetak laporan dalam format terstruktur dengan judul di atas dan setiap bagian di bawahnya. Uji dengan melempar bagian sepertiringkasan=..., rekomendasi=..., catatan=.... -
Tulis fungsi
gabung_rekap(*sesi, **konfigurasi)yang menerima beberapa dictionary statistik (hasilrekap_nilai) dan menggabungkannya menjadi statistik keseluruhan.konfigurasibisa berisitampilkan_detail=Trueuntuk mengontrol output. Bagaimana cara menghitung rata-rata dari beberapa rata-rata dengan jumlah peserta berbeda secara akurat?
*args dan **kwargs mengubah cara kita berpikir tentang kontrak sebuah fungsi: dari “fungsi ini menerima tepat tiga argumen” menjadi “fungsi ini menerima data sebanyak yang kamu punya.” Fleksibilitas ini sangat berguna saat membangun utilitas, wrapper, atau fungsi yang harus bekerja dengan data yang bentuknya belum bisa diprediksi. Selanjutnya, kita akan membahas bagaimana fungsi bisa didefinisikan di dalam fungsi lain — membuka pintu ke teknik yang lebih expressif dalam Python.