Iterators & Generators Python
Dalam dunia Python, Iterators dan Generators adalah kunci efisiensi memori. Mereka memungkinkan Anda untuk memproses data dalam jumlah besar (bahkan tak terbatas) tanpa harus memuat semuanya ke dalam RAM sekaligus.
Bayangkan Anda ingin membaca buku tebal. Iterator memungkinkan Anda membaca halaman per halaman (hanya memuat satu halaman ke memori), alih-alih mencoba menghafal buku setebal 1000 halaman sekaligus.
1. Iterators
Iterator adalah objek yang mengandung jumlah nilai yang dapat dihitung. Iterator dapat diiterasi, yang berarti Anda dapat melintasi semua nilai.
Secara teknis, di Python, iterator adalah objek yang mengimplementasikan protokol iterator, yang terdiri dari metode __iter__() dan __next__().
Contoh Membuat Iterator
Mari kita buat iterator sederhana yang mengembalikan angka dari 1 sampai batas tertentu.
class AngkaSaya:
def __init__(self, batas):
self.batas = batas
self.angka = 1
def __iter__(self):
return self
def __next__(self):
if self.angka <= self.batas:
x = self.angka
self.angka += 1
return x
else:
raise StopIteration
kelaskuu = AngkaSaya(3)
iteratorku = iter(kelaskuu)
print(next(iteratorku)) # Output: 1
print(next(iteratorku)) # Output: 2
print(next(iteratorku)) # Output: 3
# print(next(iteratorku)) # Akan error StopIteration
Saat Anda menggunakan loop for, Python secara otomatis menangani __iter__() dan exception StopIteration.
for x in AngkaSaya(3):
print(x)
2. Generators
Generator adalah cara sederhana untuk membuat iterator. Alih-alih menulis class yang panjang dengan __iter__() dan __next__(), Anda cukup mendefinisikan fungsi biasa dan menggunakan kata kunci yield di mana Anda ingin mengembalikan data.
Setiap kali yield dipanggil, fungsi akan "berhenti sementara" (pause), menyimpan semua variabelnya, dan melanjutkan dari titik tersebut saat dipanggil lagi.
Contoh Generator Sederhana
def generator_angka(batas):
angka = 1
while angka <= batas:
yield angka
angka += 1
gen = generator_angka(3)
# Generator juga adalah iterator!
print(next(gen)) # 1
print(next(gen)) # 2
print(next(gen)) # 3
Keunggulan Generator: Efisiensi Memori
Bayangkan Anda perlu memproses 1 juta angka.
Menggunakan List (Memakan Memori):
def ambil_list():
hasil = []
for i in range(1000000):
hasil.append(i)
return hasil
# Ini akan memakan memori sekitar 40MB+ untuk list integer
Menggunakan Generator (Hemat Memori):
def ambil_generator():
for i in range(1000000):
yield i
# Ini hampir tidak memakan memori tambahan, karena angka dihasilkan satu per satu saat diminta.
3. Generator Expression
Mirip dengan List Comprehension, tetapi menggunakan tanda kurung biasa (). Ini menghasilkan generator object, bukan list.
# List Comprehension (Membuat list penuh di memori)
kuadrat_list = [x**2 for x in range(10)]
print(kuadrat_list) # [0, 1, 4, ..., 81]
# Generator Expression (Lazy evaluation)
kuadrat_gen = (x**2 for x in range(10))
print(kuadrat_gen) # <generator object ...>
# Untuk melihat isinya, harus diiterasi
for i in kuadrat_gen:
print(i, end=" ")
4. Studi Kasus: Membaca File Besar
Jika Anda harus memproses file log server berukuran 10GB.
Salah (Jangan lakukan ini):
def baca_file_salah(nama_file):
file = open(nama_file)
isi = file.read() # BAHAYA! Akan memuat seluruh 10GB ke RAM.
return isi.split("\n")
Benar (Gunakan Generator):
def baca_ file_benar(nama_file):
for baris in open(nama_file):
yield baris
# Kita bisa loop file 10GB tanpa masalah memori
for baris in baca_file_benar("server.log"):
if "ERROR" in baris:
print(baris)
Kesimpulan
- Iterator: Objek yang bisa diiterasi (
__next__). - Generator: Fungsi yang menghasilkan (
yield) nilai satu per satu (lazy evaluation). - Gunakan Generator saat bekerja dengan dataset besar atau stream data tak terbatas.