โ† Semua pembelajaran / Python untuk AI Engineer
Fase 1 ยท Sintaks & Idiom Python

Functional Programming HOWTO

Memahami iterator dan generator: kenapa Python bisa memproses data yang lebih besar dari memori.

Sumber asli docs.python.org Resmi Rangkuman ~6 menit baca

Intisari

  • Iterable = bisa dilewati for. Iterator = sedang dilewati, dan hanya bisa sekali.
  • Generator (yield) memproduksi nilai satu per satu โ€” memori tetap kecil berapa pun ukuran datanya.
  • Sebagian besar fungsi bawaan Python mengembalikan iterator lazy, bukan list.
  • itertools berisi alat siap pakai: chain, islice, groupby, batched.
  • Comprehension unggul karena mudah dibaca; kalau sudah tidak terbaca, kembalikan jadi loop biasa.

Iterable vs iterator

Dua istilah ini sering tertukar, padahal bedanya penting:

IterableIterator
ArtinyaBisa dilewati oleh forSedang dalam proses dilewati
Contohlist, str, dict, setGenerator, zip(), map()
Bisa berkali-kali?YaTidak โ€” sekali habis, habis
Punya panjang?Biasanya yaTidak
angka = [1, 2, 3]        # iterable
sum(angka)               # 6
sum(angka)               # 6 โ€” masih bisa

g = (x for x in [1, 2, 3])   # iterator
sum(g)                   # 6
sum(g)                   # 0  โ† sudah habis!

Ini bug yang sering menjebak. Kalau kamu perlu melewati data dua kali, jangan simpan generator โ€” konversikan dulu: data = list(generator).

Generator

def baca_baris(path: Path):
    with path.open(encoding="utf-8") as f:
        for baris in f:
            yield baris.strip()

for baris in baca_baris(Path("besar.log")):
    proses(baris)

Fungsi apa pun yang mengandung yield otomatis jadi generator. Saat dipanggil, ia tidak menjalankan isinya sama sekali โ€” hanya mengembalikan objek generator. Kode di dalamnya baru jalan saat kamu mulai iterasi, dan berhenti (bukan selesai) di tiap yield.

Kenapa ini penting

# Memuat seluruh file ke memori โ€” 2 GB file = 2 GB RAM
baris = open("besar.log").readlines()

# Generator โ€” satu baris di memori pada satu waktu
for baris in baca_baris(Path("besar.log")):
    ...

Di pipeline RAG (Fase 7), pola ini yang menyelamatkanmu. Ingest ratusan PDF menjadi rantai generator: baca โ†’ potong jadi chunk โ†’ embed โ†’ simpan. Tidak pernah ada tahap yang memuat seluruh korpus ke memori sekaligus.

Merangkai generator

def baca(paths):
    for p in paths:
        yield p.read_text(encoding="utf-8")

def potong(dokumen, ukuran=1000):
    for teks in dokumen:
        for i in range(0, len(teks), ukuran):
            yield teks[i:i + ukuran]

def bersihkan(chunks):
    for c in chunks:
        c = c.strip()
        if c:
            yield c

# Tidak ada yang dieksekusi sampai baris terakhir mulai berjalan
pipeline = bersihkan(potong(baca(Path("docs").rglob("*.md"))))
for chunk in pipeline:
    simpan(chunk)

Fungsi bawaan yang lazy

map(f, items)             # iterator, bukan list
filter(p, items)          # iterator
zip(a, b)                 # iterator โ€” berhenti di yang terpendek
enumerate(items)          # iterator
reversed(items)           # iterator
range(1_000_000)          # tidak pernah membuat sejuta angka di memori

Kalau butuh hasilnya sebagai list, bungkus: list(map(f, items)).

Tapi: di Python, [f(x) for x in items] lebih idiomatik daripada list(map(f, items)), dan [x for x in items if p(x)] lebih idiomatik daripada list(filter(p, items)). map/filter baru menang kalau f sudah berupa fungsi bernama.

itertools โ€” yang benar-benar berguna

from itertools import chain, islice, groupby, batched

# Sambung beberapa iterable jadi satu
chain(list_a, list_b, list_c)

# Ambil 10 pertama dari iterator apa pun (slicing tidak bisa untuk generator)
islice(generator_besar, 10)

# Bagi jadi kelompok berukuran tetap โ€” Python 3.12+
for grup in batched(dokumen, 100):
    kirim_batch(grup)      # penting untuk API yang punya batas ukuran request

# Kelompokkan โ€” INPUT HARUS SUDAH TERURUT berdasarkan key yang sama
for kategori, anggota in groupby(sorted(docs, key=kunci), key=kunci):
    print(kategori, len(list(anggota)))

groupby hanya mengelompokkan yang berdampingan. Kalau input belum terurut, hasilnya akan terpecah-pecah. Untuk pengelompokan umum, defaultdict(list) lebih aman dan lebih jelas.

Kapan comprehension menjadi kurang terbaca

Bagian ini yang membuat dokumen HOWTO layak dibaca โ€” ia jujur soal batasnya:

# โœ… terbaca dalam sekali lihat
aktif = [u for u in pengguna if u.aktif]

# โš ๏ธ mulai berat
hasil = [transform(x) for sub in matriks for x in sub if valid(x) and x.skor > 0.5]

# โœ… tulis ulang jadi loop โ€” lebih panjang, jauh lebih terbaca
hasil = []
for sub in matriks:
    for x in sub:
        if valid(x) and x.skor > 0.5:
            hasil.append(transform(x))

lambda: seperlunya saja

sorted(docs, key=lambda d: d.skor)          # โœ… pas โ€” sekali pakai, sangat pendek

f = lambda x: x * 2                          # โŒ kalau diberi nama, pakai def saja
def f(x): return x * 2                       # โœ… punya nama di traceback

Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.