collections
Tiga struktur data dari modul collections yang benar-benar sering dipakai, dan cara mengenali kapan butuh masing-masing.
Intisari
Counterโ hitung frekuensi dan ambil yang terbanyak, dalam dua baris.defaultdictโ pengelompokan tanpa perlu cek โkey sudah ada belumโ.dequeโ antrean cepat di kedua ujung;maxlenotomatis membuang yang terlama.- Sisanya (
OrderedDict,ChainMap,namedtuple) jarang diperlukan di kode AI modern. - Baca sekilas saja sekarang; kembali ke sini saat ketemu masalah yang cocok.
Counter โ menghitung frekuensi
from collections import Counter
kata = "satu dua dua tiga tiga tiga".split()
c = Counter(kata)
c # Counter({'tiga': 3, 'dua': 2, 'satu': 1})
c["tiga"] # 3
c["belum ada"] # 0 โ tidak error, beda dengan dict biasa
c.most_common(2) # [('tiga', 3), ('dua', 2)]
sum(c.values()) # 6 โ total semua
Bandingkan dengan cara manual yang biasanya ditulis pendatang baru:
jumlah = {}
for k in kata:
if k not in jumlah:
jumlah[k] = 0
jumlah[k] += 1
Counter juga bisa dijumlahkan dan dikurangkan:
c1 = Counter(dokumen_a)
c2 = Counter(dokumen_b)
c1 + c2 # gabungkan hitungan
c1 - c2 # selisih (hasil negatif dibuang)
c1 & c2 # ambil yang terkecil per key โ irisan
Di mana ini terpakai di roadmap
# Cari chunk mana yang paling sering dipakai sebagai sitasi (Fase 7)
sitasi = Counter(c.chunk_id for jawaban in riwayat for c in jawaban.sitasi)
print(sitasi.most_common(10))
# Cari model mana yang paling banyak dipanggil
Counter(log.model for log in panggilan_llm)
defaultdict โ pengelompokan tanpa cek
from collections import defaultdict
# Tanpa defaultdict
grup = {}
for d in dokumen:
if d.kategori not in grup:
grup[d.kategori] = []
grup[d.kategori].append(d)
# Dengan defaultdict
grup = defaultdict(list)
for d in dokumen:
grup[d.kategori].append(d) # list dibuat otomatis saat key pertama disentuh
Argumennya adalah fungsi yang dipanggil untuk membuat nilai default:
defaultdict(list) # [] untuk key baru
defaultdict(int) # 0 โ berguna untuk menghitung
defaultdict(set) # set() kosong
defaultdict(dict) # {}
Efek samping yang perlu diingat: sekadar membaca key yang belum ada akan
membuatnya. if grup["belum-ada"]: menambahkan entri kosong ke dict.
Kalau kamu hanya mau membaca tanpa efek samping, pakai grup.get(key).
deque โ antrean dua arah
from collections import deque
d = deque([1, 2, 3])
d.append(4) # kanan
d.appendleft(0) # kiri โ O(1); list.insert(0, x) itu O(n)
d.pop() # dari kanan
d.popleft() # dari kiri
Kegunaan paling praktis: buffer dengan panjang tetap.
# Simpan hanya 20 pesan terakhir โ yang lama otomatis terbuang
riwayat = deque(maxlen=20)
riwayat.append({"role": "user", "content": pesan})
riwayat.append({"role": "assistant", "content": jawaban})
messages = list(riwayat) # siap dikirim ke API LLM
Pola ini langsung terpakai di Fase 4. API LLM itu stateless โ kamu yang mengirim ulang
riwayat percakapan tiap kali. deque(maxlen=N) memberi kamu jendela geser gratis,
tanpa perlu menulis logika pemangkasan sendiri.
Sisanya: kenali saja namanya
| Nama | Fungsinya | Perlu? |
|---|---|---|
namedtuple | Tuple dengan nama field | Pakai NamedTuple dari typing atau @dataclass saja |
OrderedDict | Dict yang menjaga urutan | Tidak โ dict biasa sudah menjaga urutan sejak 3.7 |
ChainMap | Tumpukan dict sebagai satu view | Jarang |
UserDict / UserList | Dasar untuk membuat subclass | Jarang |
Bonus: functools.lru_cache
Bukan dari modul collections, tapi sering dibutuhkan bersamaan:
from functools import lru_cache
@lru_cache(maxsize=1024)
def embedding(teks: str) -> tuple[float, ...]:
return panggil_api_embedding(teks) # dipanggil sekali per teks unik
Syaratnya: semua argumen harus hashable (str, int, tuple,
frozenset) โ list dan dict tidak bisa. Itu sebabnya contoh di atas
mengembalikan tuple, bukan list.
Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.