Saya sering lihat orang menulis loop bertingkat cuma untuk menghasilkan kombinasi data, atau membangun list raksasa yang dipakai sekali lalu dibuang. itertools menangani dua-duanya. Modul bawaan Python ini berisi alat iterator yang bekerja secara lazy: data diproses satu per satu saat dibutuhkan, bukan dimuat sekaligus ke RAM. Kalau konsep dasarnya belum kamu pegang, baca dulu generator dan iterator sebelum lanjut.
Kenapa iterator, bukan list?
Bedanya gampang. List menyimpan semua elemen sekaligus di memori. Iterator cuma tahu cara menghasilkan elemen berikutnya. Buat data sepuluh juta elemen, list bisa makan ratusan megabyte, sementara iterator hampir tidak pakai memori karena elemen dibuat saat diminta.
import sys
# List: semua elemen langsung ada di RAM
data = [x * x for x in range(10_000_000)]
print(sys.getsizeof(data)) # puluhan MB
# Iterator: elemen dibuat saat diminta
gen = (x * x for x in range(10_000_000))
print(sys.getsizeof(gen)) # cuma puluhan byte
Iterator tak terbatas: count, cycle, repeat
Tiga alat ini menghasilkan aliran tanpa akhir. Jangan panggil list() langsung ke sana kecuali kamu memang mau membekukan program. Biasanya dipasangkan dengan islice untuk membatasi berapa banyak yang diambil.
from itertools import count, cycle, repeat, islice
# count(start, step): angka naik terus
for i in islice(count(10, 2), 5):
print(i) # 10, 12, 14, 16, 18
# cycle: ulangi koleksi tanpa henti
lampu = cycle(["merah", "kuning", "hijau"])
print([next(lampu) for _ in range(7)])
# ['merah', 'kuning', 'hijau', 'merah', 'kuning', 'hijau', 'merah']
# repeat: nilai yang sama terus-menerus
print(list(repeat("latihan", 3))) # ['latihan', 'latihan', 'latihan']
Menggabungkan dan memotong: chain, islice, compress
chain menempelkan beberapa iterable jadi satu aliran tanpa membuat list gabungan di memori. islice memotong iterable seperti slicing pada list, tapi tetap lazy. compress memilih elemen berdasarkan mask boolean.
from itertools import chain, compress, islice
# chain: gabung tanpa list baru
a = [1, 2, 3]
b = (4, 5)
c = {6, 7}
print(list(chain(a, b, c))) # [1, 2, 3, 4, 5, 6, 7]
# islice(iterable, start, stop, step)
semua = range(100)
print(list(islice(semua, 5, 15, 2))) # [5, 7, 9, 11, 13]
# compress: ambil elemen yang mask-nya True
nilai = ["a", "b", "c", "d"]
mask = [True, False, True, False]
print(list(compress(nilai, mask))) # ['a', 'c']
Kombinasi dan permutasi: product, permutations, combinations
Tiga alat ini berguna buat brute force, membangun test case, atau analisis kombinasi. product menghasilkan Cartesian product, permutations semua susunan yang memperhatikan urutan, combinations semua pilihan yang mengabaikan urutan.
from itertools import product, permutations, combinations
# product: semua pasangan dari dua himpunan
for a, b in product("AB", "12"):
print(a + b, end=" ") # A1 A2 B1 B2
# permutations: urutan diperhitungkan
print(list(permutations("ABC", 2)))
# [('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'C'), ('C', 'A'), ('C', 'B')]
# combinations: urutan diabaikan
print(list(combinations("ABC", 2)))
# [('A', 'B'), ('A', 'C'), ('B', 'C')]
Groupby: kelompokkan data yang berurutan
groupby mengelompokkan elemen yang berdekatan dan berbagi kunci sama. Gotcha terbesarnya: dia tidak mengurutkan apa pun. Data harus diurutkan dulu, kalau tidak kelompok yang sama akan pecah jadi beberapa bagian. Ini beda dengan groupby di pandas yang mengumpulkan dari seluruh DataFrame.
from itertools import groupby
transaksi = [
("bayar", 50_000),
("bayar", 75_000),
("refund", 20_000),
("bayar", 30_000),
("refund", 10_000),
]
# urutkan dulu, baru groupby
transaksi.sort(key=lambda x: x[0])
for jenis, grup in groupby(transaksi, key=lambda x: x[0]):
total = sum(nominal for _, nominal in grup)
print(f"{jenis}: {total}")
# bayar: 155000
# refund: 30000
Studi kasus: deteksi pasangan transaksi mencurigakan
Sekarang gabungkan beberapa alat jadi satu kasus nyata. Kamu punya daftar transaksi dengan waktu masing-masing, dan ingin mencari semua pasangan yang terjadi dalam jarak beberapa detik — pola umum di deteksi fraud sederhana. combinations menghasilkan pasangan, lalu kita filter selisih waktunya.
from itertools import combinations
from datetime import datetime, timedelta
transaksi = [
(101, datetime(2026, 10, 19, 9, 0, 0)),
(102, datetime(2026, 10, 19, 9, 0, 5)),
(103, datetime(2026, 10, 19, 9, 15, 0)),
(104, datetime(2026, 10, 19, 9, 15, 4)),
]
batas = timedelta(seconds=10)
mencurigakan = []
for (id_a, t_a), (id_b, t_b) in combinations(transaksi, 2):
if abs(t_a - t_b) <= batas:
mencurigakan.append((id_a, id_b))
print(mencurigakan) # [(101, 102), (103, 104)]
Kalau datamu jutaan baris, jangan pakai combinations untuk semua pasangan — jumlahnya meledak jadi n². Urutkan berdasarkan waktu lalu geser dengan pointer atau islice. itertools tetap jadi fondasinya, tapi pilih alat yang pas untuk skala datamu.
Modul ini kecil tapi padat. Begitu terbiasa berpikir dalam iterator, kamu jarang lagi menulis loop bersarang manual. Lanjut ke functools yang punya alat serupa untuk fungsi, lalu coba tulis ulang satu script lama kamu memakai itertools dan bandingkan panjangnya.