Tidak semua hasil pengolahan data perlu disimpan. Saat menghitung sum(), max(), atau any(), nilai-nilai sumber cukup dilewati sekali lalu dibuang — membangun list perantara berisi jutaan elemen hanya untuk dijumlahkan adalah pemborosan. Generator expression menyediakan sintaks comprehension yang sama persis, tetapi menghasilkan aliran nilai lazy: nilai dihitung hanya saat dibutuhkan, satu per satu.
Sintaks untuk Agregasi #
Generator expression ditulis di dalam tanda kurung biasa: (ekspresi for item in iterable). Tempat favoritnya adalah menjadi argumen fungsi agregasi:
total = sum(n * n for n in range(1, 11))
print(total)
harga = [150000, 85000, 320000]
termahal = max(h for h in harga)
print(termahal)
ada_murah = any(h < 100000 for h in harga)
print(ada_murah)Output yang Dihasilkan:
385
320000
TrueMari kita bedah baris pertama (sum(n * n for n in range(1, 11))):
- Bagian
for n in range(1, 11): interpreter menyiapkan resep pengambilan angka1sampai10— bukan list angkanya, hanya resepnya. - Bagian
n * n: setiap angka yang diambil akan dikuadratkan, sekali lagi sebagai resep. - Fungsi
sum(): sum meminta nilai satu per satu dari generator, menjumlahkan, lalu membuang setiap nilai setelah dipakai. - Hasil akhir: angka
385— penjumlahan kuadrat 1 sampai 10 — tanpa list perantara pernah ada di memori.
Evaluasi Lazy dan Sifat Sekali Jalan #
Generator expression menghasilkan sebuah generator object — objek yang menyimpan resep perhitungan beserta posisi baca saat ini. Nilai tidak dihitung di awal; setiap permintaan (dari sum(), max(), atau loop for) memicu perhitungan tepat satu nilai berikutnya.
Konsekuensi pentingnya: generator object habis sekali jalan, seperti kartu yang sudah dibaca semuanya.
angka = [5, 3, 8, 1]
genexp = (n * 10 for n in angka)
print(sum(genexp))
print(sum(genexp))Output yang Dihasilkan:
170
0Penjelasan dua baris hasil tersebut:
sum(genexp)pertama: sum menarik50,30,80,10satu per satu sampai generator kosong, menghasilkan170.sum(genexp)kedua: posisi baca sudah di ujung, tidak ada nilai tersisa sama sekali — sum dari aliran kosong adalah0.
Memori List versus Generator #
Module sys menyediakan fungsi sys.getsizeof() untuk melihat besar memori sebuah objek. Perbandingan berikut mengukur list comprehension dan generator expression dari sumber yang sama:
import sys
list_100 = [n * n for n in range(100)]
list_10000 = [n * n for n in range(10_000)]
genexp_100 = (n * n for n in range(100))
genexp_10000 = (n * n for n in range(10_000))
print("list 100 elemen :", sys.getsizeof(list_100), "byte")
print("list 10000 elemen :", sys.getsizeof(list_10000), "byte")
print("genexp 100 elemen :", sys.getsizeof(genexp_100), "byte")
print("genexp 10000 elemen:", sys.getsizeof(genexp_10000), "byte")Output yang Dihasilkan:
list 100 elemen : 920 byte
list 10000 elemen : 85176 byte
genexp 100 elemen : 200 byte
genexp 10000 elemen: 200 byteUkuran list tumbuh seiring jumlah elemen: dari 920 byte untuk 100 elemen menjadi 85176 byte untuk 10.000 elemen. Ukuran generator tetap 200 byte pada keduanya — generator hanya menyimpan resep (n * n), sumber (range), dan posisi baca; nilainya sendiri belum pernah dibuat.
Kapan memilih yang mana? Ringkasannya:
| Kebutuhan | Pilihan tepat |
|---|---|
| Hasil disimpan, diindeks, atau diiterasi berkali-kali | list comprehension |
Sekadar dijumlahkan/dicari maksimum/diuji any-all sekali jalan | generator expression |
| Sumber sangat besar atau tak terbatas (aliran data, sensor) | generator expression |
Untuk puluhan atau ratusan elemen, selisih memori tidak terasa dan keduanya sama baiknya. Keunggulan generator muncul pada data besar — dan pada bab selanjutnya kita akan melihat bahwa mekanisme lazy seperti ini bukan trik khusus, melainkan bagian dari protokol iterator yang menjadi mesin semua loop di Python.