Fungsi biasa menghitung semua hasil sekaligus, menyimpannya dalam list, lalu mengembalikan list tersebut. Untuk satu juta nilai, satu juta nilai itu harus berdiri di memori pada saat yang bersamaan — padahal sering kali kita hanya perlu melihat satu nilai pada satu waktu. Generator function menyelesaikan masalah ini dengan kata kunci yield: fungsi berhenti di tengah jalan, menyerahkan satu nilai, lalu melanjutkan dari titik berhentinya ketika diminta lagi.

yield Menjeda, return Mengakhiri #

Perbedaan fundamental keduanya: return memutus fungsi selamanya, sedangkan yield hanya menjeda fungsi dan menyimpan seluruh keadaannya — posisi eksekusi beserta seluruh variabel lokalnya. Perhatikan generator function pertama kita:

python
def buat_nomor_pesanan():
    nomor = 100
    print("badan generator mulai berjalan")
    yield nomor
    nomor += 1
    yield nomor
    nomor += 1
    yield nomor
    print("badan generator selesai")

pesanan = buat_nomor_pesanan()
print("tipe objek:", type(pesanan).__name__)
print("nilai #1  :", next(pesanan))
print("nilai #2  :", next(pesanan))
print("nilai #3  :", next(pesanan))
try:
    next(pesanan)
except StopIteration:
    print("nilai #4  : StopIteration dilempar")

Output yang Dihasilkan:

text
tipe objek: generator
badan generator mulai berjalan
nilai #1  : 100
nilai #2  : 101
nilai #3  : 102
badan generator selesai
nilai #4  : StopIteration dilempar

Mari kita bedah kronologinya baris per baris:

  1. Baris 11 (pesanan = buat_nomor_pesanan()): memanggil fungsi tersebut tidak menjalankan badannya. Interpreter hanya membuat sebuah objek bertipe generator — buktinya, keluaran tipe objek: generator tercetak lebih dulu daripada "badan generator mulai berjalan".
  2. Baris 13 (print("nilai #1 :", next(pesanan))): fungsi built-in next() menekan tombol jalan. Badan fungsi baru mulai berjalan sampai bertemu yield nomor, menyerahkan nilai 100, lalu berhenti di baris tersebut.
  3. Baris 14 (print("nilai #2 :", next(pesanan))): eksekusi lanjut dari baris yield pertama — perhatikan bahwa nomor masih bernilai 100 dan bertambah menjadi 101 sebelum diserahkan.
  4. Baris 16-18 (except StopIteration:): setelah yield ketiga, next() keempat membuat badan fungsi berjalan sampai selesai tanpa menemui yield lagi. Interpreter Python melempar StopIteration — sinyal standar bahwa aliran nilai telah habis, sama seperti yang sudah kita kenal pada Chapter iter() dan next().

for Loop Menjalankan next() di Balik Layar #

Kita hampir tidak pernah memanggil next() secara manual, karena loop for melakukannya untuk kita: loop menarik satu nilai, menjalankan badan loop, menarik nilai berikutnya, dan berhenti secara otomatis ketika StopIteration muncul:

python
def hitung_mundur(dari):
    print(f"mulai berhitung dari {dari}")
    while dari > 0:
        yield dari
        dari -= 1
    print("hitungan selesai")

for angka in hitung_mundur(3):
    print("tick:", angka)

Output yang Dihasilkan:

text
mulai berhitung dari 3
tick: 3
tick: 2
tick: 1
hitungan selesai

Urutan keluaran membuktikan penggantian kendali: baris mulai berhitung tercetak sekali, tick muncul tiga kali bergantian dengan pembaruan dari, dan hitungan selesai tercetak saat next() keempat menemukan kondisi while yang gagal. Variabel dari bertahan di antara yield — keadaan fungsi tidak pernah direset selama generator masih hidup.

List versus Generator di Memori #

Bandingkan dua fungsi yang memproduksi satu juta nomor: satu mengembalikan list, satu mengembalikannya lewat yield:

python
import sys

def nomor_list(total):
    hasil = []
    nomor = 0
    for _ in range(total):
        nomor += 1
        hasil.append(nomor)
    return hasil

def nomor_generator(total):
    nomor = 0
    for _ in range(total):
        nomor += 1
        yield nomor

daftar = nomor_list(1_000_000)
aliran = nomor_generator(1_000_000)

print("list 1 juta nomor :", sys.getsizeof(daftar), "byte")
print("generator 1 juta  :", sys.getsizeof(aliran), "byte")
print("elemen ke-1000    :", daftar[999])
print("jumlah aliran     :", sum(aliran))

Output yang Dihasilkan:

text
list 1 juta nomor : 8448728 byte
generator 1 juta  : 208 byte
elemen ke-1000    : 1000
jumlah aliran     : 500000500000
  1. 8448728 byte: list menampung satu juta referensi sekaligus di memori — dan itu baru biaya wadahnya; objek angka di dalamnya menambah lagi.
  2. 208 byte: generator hanya menyimpan resep (kode), posisi eksekusi, dan variabel nomor — berapapun jumlah total-nya, ukurannya tetap.
  3. daftar[999]: list mendukung pengindeksan dan panjang len(). Generator tidak mendukung keduanya; generator adalah aliran sekali jalan.

Kita sudah melihat versi ringkas mekanisme ini pada Chapter Generator Expressions dalam bentuk (ekspresi for item in ...). Generator function adalah bentuk penuhnya: logika bertingkat, kondisi kompleks, dan keadaan internal bisa ditulis bebas di dalam badan fungsi. Selanjutnya kita telusuri siklus hidup generator itu sendiri.