Episode ini memperdalam fungsi Python: argumen posisi dan keyword, *args dan **kwargs, serta jebakan default mutable. Kalian juga belajar higher-order functions, lambda, modul functools untuk partial dan lru_cache, dan utilitas iterator.

Di episode 3 kita mengenal fungsi dasar. Episode 4 membawa kalian lebih dalam: bagaimana fungsi Python yang sesungguhnya bekerja di dunia nyata — termasuk jebakan tersembunyi yang bisa membuat kode kalian berperilaku aneh.
Kita akan membahas argumen fleksibel dengan *args dan **kwargs, jebakan default mutable yang terkenal, higher-order functions, lambda, modul functools, dan utilitas iterator. Kemampuan ini membedakan programmer Python biasa dari yang mahir.
*args menangkap jumlah argumen posisi yang fleksibel sebagai tuple:
def jumlahkan(*args):
return sum(args)
print(jumlahkan(1, 2, 3))
print(jumlahkan(10, 20))Pemanggilan jumlahkan(1, 2, 3) mengumpulkan semua argumen posisi ke dalam tuple args. Ini berguna saat jumlah argumen tidak pasti, seperti fungsi log yang menerima pesan berapa pun.
**kwargs menangkap argumen keyword yang fleksibel sebagai dict:
def cetak_profil(**kwargs):
for kunci, nilai in kwargs.items():
print(f"{kunci}: {nilai}")
cetak_profil(nama="Arman", peran="Engineer", tim="Platform")Pemanggilan cetak_profil(nama="Arman", peran="Engineer") mengumpulkan keyword argument ke dalam dict. **kwargs sangat umum dipakai untuk meneruskan opsi konfigurasi tanpa mendeklarasikan setiap parameter.
Jebakan klasik Python adalah memakai objek mutable sebagai nilai default. Default dievaluasi satu kali saat fungsi didefinisikan, bukan setiap kali dipanggil:
def tambah(item, daftar=[]):
daftar.append(item)
return daftar
print(tambah(1))
print(tambah(2))Output kedua memanggil tambah(2) dan mengembalikan [1, 2], bukan [2]! Karena daftar=[] dibuat sekali dan dibagikan antar panggilan. Ini bug terkenal yang sulit dilacak.
Solusi standarnya adalah memakai None sebagai default lalu membuat objek baru di dalam fungsi:
def tambah(item, daftar=None):
if daftar is None:
daftar = []
daftar.append(item)
return daftar
print(tambah(1))
print(tambah(2))Pola if daftar is None: daftar = [] menjamin objek baru dibuat setiap panggilan. Aturan emasnya: jangan pernah memakai list, dict, atau set sebagai default parameter.
Python memperlakukan fungsi sebagai objek kelas satu: fungsi bisa disimpan, dikirim sebagai argumen, dan dikembalikan. Fungsi yang menerima atau mengembalikan fungsi disebut higher-order function:
def terapkan(fungsi, nilai):
return fungsi(nilai)
def kali_dua(x):
return x * 2
print(terapkan(kali_dua, 5))Pemanggilan terapkan(kali_dua, 5) mengirim fungsi kali_dua sebagai argumen. Pola ini menjadi dasar dari banyak teknik functional programming dan digunakan luas di library seperti pandas dan Django.
Lambda adalah fungsi anonim satu baris untuk kasus sederhana:
kuadrat = lambda x: x * x
print(kuadrat(7))
data = [(1, "apel"), (3, "ceri"), (2, "mangga")]
data.sort(key=lambda pasangan: pasangan[1])
print(data)lambda x: x * x mendefinisikan fungsi tanpa nama. Saat mengurutkan, data.sort(key=lambda pasangan: pasangan[1]) mengurutkan berdasarkan elemen kedua. Gunakan lambda untuk logika singkat; untuk logika kompleks, definisikan fungsi bernama.
functools.partial mengunci sebagian argumen sebuah fungsi menjadi fungsi baru:
from functools import partial
def pangkat(eksponen, basis):
return basis ** eksponen
kuadrat = partial(pangkat, 2)
print(kuadrat(5))partial(pangkat, 2) membuat fungsi baru yang sudah mengunci eksponen=2. Memanggil kuadrat(5) setara dengan pangkat(2, 5). Ini berguna untuk membangun fungsi khusus dari fungsi umum.
lru_cache menyimpan hasil panggilan fungsi sehingga panggilan berulang dengan argumen sama menjadi instan:
from functools import lru_cache
@lru_cache(maxsize=128)
def fibonacci(n):
if n < 2:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
print(fibonacci(30))Dekorator @lru_cache(maxsize=128) menambahkan cache ke fungsi fibonacci. Tanpa cache, perhitungan ke-30 berulang jutaan kali; dengan cache, setiap nilai dihitung sekali. Memoisasi ini sangat efektif untuk fungsi rekursif dan query berulang.
Modul itertools menyediakan fungsi pembangun iterator yang efisien. Beberapa yang paling berguna:
from itertools import chain, islice, cycle
angka = [1, 2, 3]
huruf = ["a", "b"]
print(list(chain(angka, huruf)))
print(list(islice(cycle(angka), 5)))chain(angka, huruf) menggabungkan iterable menjadi satu. islice(cycle(angka), 5) memutar iterable secara tak terbatas lalu memotong lima elemen pertama. Utilitas ini hemat memory karena semua bekerja secara lazy.
Inti yang harus dibawa pulang:
Di episode 5 selanjutnya kita akan membahas tipe data lanjutan dan koleksi — modul collections dengan deque, defaultdict, Counter, dan namedtuple, dasar typing, serta pertimbangan memory dan performa struktur data. Kalian akan belajar memilih struktur data yang tepat untuk masalah nyata!