Sejak Section Control Flow, kita menjalankan for di atas list, string, dictionary, dan range tanpa pernah bertanya bagaimana caranya loop mengetahui elemen mana yang harus diambil berikutnya. Jawabannya ada pada sebuah kontrak internal bernama iterator protocol — kesepakatan dua peran antara iterable dan iterator yang menjadi mesin setiap perulangan di Python. Memahami protokol ini menjelaskan banyak hal yang selama ini kita terima begitu saja.
Dua Peran dalam Protokol #
Peran pertama adalah iterable: objek yang isinya bisa diulang — list, string, tuple, dictionary, set, dan range. Peran kedua adalah iterator: objek kursor yang sedang berjalan menelusuri isi sebuah iterable, selalu tahu posisi baca saat ini, dan mampu mengambil elemen berikutnya.
| Aspek | Iterable | Iterator |
|---|---|---|
| Peran | sumber data yang bisa diulang | kursor yang sedang berjalan |
| Method kunci | __iter__ | __next__ (dan __iter__) |
| Contoh | list, str, tuple, dict, set | list_iterator, tuple_iterator |
| Diiterasi berkali-kali? | ya — setiap for membuat kursor baru | tidak — habis sekali jalan |
Fungsi iter() menjembatani kedua peran: diberi iterable, fungsi iter() mengembalikan iterator segar. Fungsi type() memperlihatkan bahwa keduanya memang objek yang berbeda:
angka = [10, 20, 30]
koordinat = (3, 5)
print(type(angka).__name__)
print(type(iter(angka)).__name__)
print(type(koordinat).__name__)
print(type(iter(koordinat)).__name__)Output yang Dihasilkan:
list
list_iterator
tuple
tuple_iteratorList angka adalah iterable, tetapi bukan iterator — list tidak punya penanda posisi baca. Setiap panggilan iter(angka) memproduksi list_iterator baru, dan kursor inilah yang benar-benar berjalan menelusuri elemen.
Bagaimana for Loop Bekerja #
Di balik layar, interpreter Python menulis ulang setiap for item in buah: menjadi urutan panggilan protokol: buat iterator dengan iter(), ambil elemen dengan next(), dan berhenti saat exception StopIteration terangkat. Perbandingan berikut menuliskan kedua bentuk tersebut secara berdampingan:
buah = ["apel", "jeruk", "mangga"]
# Bentuk yang biasa kita tulis
for item in buah:
print(item)
print("---")
# Bentuk yang sebenarnya dijalankan interpreter
iterator = iter(buah)
while True:
try:
item = next(iterator)
except StopIteration:
break
print(item)Output yang Dihasilkan:
apel
jeruk
mangga
---
apel
jeruk
manggaKedua bagian menghasilkan output identik. Bedah versi yang diperluas:
- Baris 10 (
iterator = iter(buah)): interpreter membuat kursorlist_iteratoryang siap membaca dari awal list. - Baris 13 (
item = next(iterator)):next()meminta elemen pada posisi baca saat ini, lalu memajukan kursor satu langkah. - Baris 14-15 (
except StopIteration: break): saat kursor melewati elemen terakhir,next()tidak mengembalikanNone— kursor justru mengangkat exceptionStopIterationsebagai tanda resmi “data habis”, dan loop pun berhenti. - Kesetaraan penuh:
break,continue, hinggaelsepadaforbekerja sama persis pada kedua bentuk.
Iterator Habis Sekali Jalan #
Kursor tidak punya tombol rewind. Setelah StopIteration terjadi, iterator tetap kosong selamanya — mengulang loop yang sama di atas iterator yang sama tidak menghasilkan apa-apa:
angka = [10, 20, 30]
iterator = iter(angka)
print("iterasi pertama:")
for item in iterator:
print(item)
print("iterasi kedua:")
for item in iterator:
print(item)
print("list aslinya masih utuh:")
for item in angka:
print(item)Output yang Dihasilkan:
iterasi pertama:
10
20
30
iterasi kedua:
list aslinya masih utuh:
10
20
30Iterasi kedua langsung dilewati tanpa satu baris pun tercetak, karena kursor iterator sudah berada di ujung sejak loop pertama selesai. Lalu mengapa iterasi ketiga di atas angka tetap berjalan penuh? Karena loop for selalu memanggil iter() lebih dulu — dan iter(angka) menghadirkan kursor baru yang mulai lagi dari elemen pertama.
Dua fungsi yang tadi bekerja di belakang layar — iter() dan next() — sengaja diekspos sebagai fungsi bawaan. Chapter berikutnya menggunakannya secara manual untuk kendali penuh atas proses iterasi.