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:

python
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:

text
385
320000
True

Mari kita bedah baris pertama (sum(n * n for n in range(1, 11))):

  1. Bagian for n in range(1, 11): interpreter menyiapkan resep pengambilan angka 1 sampai 10 — bukan list angkanya, hanya resepnya.
  2. Bagian n * n: setiap angka yang diambil akan dikuadratkan, sekali lagi sebagai resep.
  3. Fungsi sum(): sum meminta nilai satu per satu dari generator, menjumlahkan, lalu membuang setiap nilai setelah dipakai.
  4. 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.

python
angka = [5, 3, 8, 1]

genexp = (n * 10 for n in angka)
print(sum(genexp))
print(sum(genexp))

Output yang Dihasilkan:

text
170
0

Penjelasan dua baris hasil tersebut:

  1. sum(genexp) pertama: sum menarik 50, 30, 80, 10 satu per satu sampai generator kosong, menghasilkan 170.
  2. sum(genexp) kedua: posisi baca sudah di ujung, tidak ada nilai tersisa sama sekali — sum dari aliran kosong adalah 0.

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:

python
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:

text
list 100 elemen   : 920 byte
list 10000 elemen : 85176 byte
genexp 100 elemen : 200 byte
genexp 10000 elemen: 200 byte

Ukuran 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:

KebutuhanPilihan tepat
Hasil disimpan, diindeks, atau diiterasi berkali-kalilist comprehension
Sekadar dijumlahkan/dicari maksimum/diuji any-all sekali jalangenerator 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.