Soal 1Apa kecocokan terdekat untuk print(type(gen)) di sini?def f():
yield 1
yield 2
gen = f()
print(type(gen))
Generator Function dengan yield — Menghasilkan Nilai Satu per Satu untuk Menghemat Memori
Pelajari generator function Python dengan yield untuk mengembalikan nilai satu per satu dan menghemat memori.
Pada artikel sebelumnya kamu sudah melihat closure — cara menutup state di dalam fungsi dan mengembalikan nilai berbeda pada setiap panggilan. Python memiliki mekanisme khusus untuk fungsi semacam "kembalikan nilai berikutnya pada setiap panggilan" ini: generator function. Pakai yield alih-alih return, dan fungsi akan berhenti di tengah jalan untuk mengembalikan nilai, lalu melanjutkan dari titik berhenti pada panggilan berikutnya.
Ini cocok ketika kamu tidak ingin memuat koleksi besar sekaligus dan lebih suka mengalirkan satu nilai pada satu waktu — pemrosesan log, pengolahan data massal, dan workload serupa.
Dasar yield — Mengembalikan Satu Nilai pada Satu Waktu
Fungsi yang memiliki yield value di badannya adalah sebuah generator function. Memanggilnya seperti fungsi biasa tidak menjalankan badan — kamu hanya mendapat balik objek generator yang spesial.
Untuk benar-benar mengambil nilainya, panggil next(obj).
next() pertama menjalankan badan sampai yield pertama dan mengembalikan nilai itu.
next() berikutnya melanjutkan dari sana dan berjalan sampai yield berikutnya.
Begitu tidak ada lagi yield yang tersisa, next() lain akan memunculkan StopIteration.
def simple():
yield 1
yield 2
gen = simple()
print(type(gen)) # <class 'generator'>
print(next(gen)) # 1
print(next(gen)) # 2
print(next(gen)) # 3
# print(next(gen)) ← StopIteration (tidak ada yield lagi)
Beda dengan return
Fungsi biasa return dan seluruh scope-nya hilang — panggilan berikutnya dimulai dari atas. Generator function, sebaliknya, berhenti di yield dan menyimpan local serta posisinya. next() berikutnya melanjutkan tepat di tempat kamu berhenti, dan itulah perbedaan besarnya.
Mengambil Nilai dengan for — Tidak Perlu Khawatir Soal StopIteration
Menulis next() setiap saat itu melelahkan, dan kamu juga tidak seharusnya menangani StopIteration sendiri. Pakai for value in generator: sebagai gantinya, dan Python memanggil next() di balik layar dan keluar dari loop secara otomatis saat generator selesai. Ini bentuk yang jauh lebih umum.
Jika kamu menyebar baris print("sedang berjalan...") di kedua sisi, kamu bisa melihat setiap yield mengalihkan eksekusi bolak-balik — sisi generator → sisi pemanggil → sisi generator.
def count_up_to(max_value):
print("memulai generator")
for i in range(max_value):
print(f" sebelum yield: {i}")
yield i
print(f" sesudah yield: {i}")
for v in count_up_to(3):
print(f"diterima: {v}")
# Alur output:
# memulai generator
# sebelum yield: 0
# diterima: 0
# sesudah yield: 0
# sebelum yield: 1
# ... (berlanjut)
Beda dengan list — Memangkas Penggunaan Memori
Saat kamu bekerja dengan nilai dari 0 hingga 999_999, list comprehension memuat seluruh 1.000.000 integer sekaligus di memori. Generator, sebaliknya, hanya menyimpan nilai saat ini dan menghitung yang berikutnya sesuai permintaan. List akan berakhir di kisaran beberapa MB; objek generator itu sendiri hanya beberapa ratus byte.
import sys
MAX = 10 ** 6
# List: memuat semuanya ke memori sekaligus
data_list = [i for i in range(MAX)]
print(sys.getsizeof(data_list))
# misalnya 8000056 (~8 MB)
# Generator expression: hanya item saat ini
data_gen = (i for i in range(MAX))
print(sys.getsizeof(data_gen))
# misalnya ~200 byte
# Kode di sisi pemanggil identik
for v in data_gen:
if v > 2:
break
print(v)
# 0
# 1
# 2
Pintasan Generator Expression
Tukar tanda kurung siku [ ... ] pada list comprehension dengan tanda kurung biasa ( ... ) dan kamu mendapatkan generator expression. (i for i in range(1_000_000)) memberi efek yang sama dengan generator function berbasis def dalam satu baris. Teruskan langsung ke sum() / max() / any() dan kawan-kawan — semua bekerja.
Merantai Generator dengan yield from
Saat kamu ingin generator meneruskan nilai dari generator lain apa adanya, kamu bisa menulis yield from sub_generator dalam satu baris alih-alih for v in sub: yield v. Ini berguna saat kamu ingin menggabungkan beberapa sumber data menjadi satu generator.
Misalnya, dengan fungsi yang mengalirkan penjualan untuk cabang Tokyo dan satu lagi untuk Osaka, menjajarkan yield from tokyo_sales() dan yield from osaka_sales() memberikan pemanggil generator yang terlihat seperti satu aliran berkelanjutan.
def tokyo_sales():
yield 1200
yield 980
def osaka_sales():
yield 850
yield 1340
def all_sales():
yield from tokyo_sales()
yield from osaka_sales()
for amount in all_sales():
print(amount)
# 1200
# 980
# 850
# 1340
yield from sub_gen() adalah kependekan dari for v in sub_gen(): yield v.
Nilai yang di-yield oleh sub langsung sampai ke pemanggil luar, sehingga
1200, 980 dari tokyo_sales,
lalu 850, 1340 dari osaka_sales
tiba di for amount in all_sales(): secara berurutan.
Cek Pemahaman
Jawab setiap pertanyaan satu per satu.
Soal 2Apa yang terjadi saat kamu memanggil next() pada generator setelah setiap yield sudah habis?
Soal 3Manakah dari dua baris ini yang memakai memori jauh lebih sedikit?
A: data = [i for i in range(10**6)]
B: data = (i for i in range(10**6))