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:
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:
tipe objek: generator
badan generator mulai berjalan
nilai #1 : 100
nilai #2 : 101
nilai #3 : 102
badan generator selesai
nilai #4 : StopIteration dilemparMari kita bedah kronologinya baris per baris:
- Baris 11 (
pesanan = buat_nomor_pesanan()): memanggil fungsi tersebut tidak menjalankan badannya. Interpreter hanya membuat sebuah objek bertipegenerator— buktinya, keluarantipe objek: generatortercetak lebih dulu daripada"badan generator mulai berjalan". - Baris 13 (
print("nilai #1 :", next(pesanan))): fungsi built-innext()menekan tombol jalan. Badan fungsi baru mulai berjalan sampai bertemuyield nomor, menyerahkan nilai100, lalu berhenti di baris tersebut. - Baris 14 (
print("nilai #2 :", next(pesanan))): eksekusi lanjut dari barisyieldpertama — perhatikan bahwanomormasih bernilai100dan bertambah menjadi101sebelum diserahkan. - Baris 16-18 (
except StopIteration:): setelahyieldketiga,next()keempat membuat badan fungsi berjalan sampai selesai tanpa menemuiyieldlagi. Interpreter Python melemparStopIteration— 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:
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:
mulai berhitung dari 3
tick: 3
tick: 2
tick: 1
hitungan selesaiUrutan 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:
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:
list 1 juta nomor : 8448728 byte
generator 1 juta : 208 byte
elemen ke-1000 : 1000
jumlah aliran : 5000005000008448728 byte: list menampung satu juta referensi sekaligus di memori — dan itu baru biaya wadahnya; objek angka di dalamnya menambah lagi.208 byte: generator hanya menyimpan resep (kode), posisi eksekusi, dan variabelnomor— berapapun jumlahtotal-nya, ukurannya tetap.daftar[999]: list mendukung pengindeksan dan panjanglen(). 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.