Python & AI Tutorials Logo
Pemrograman Python

35. Cara Kerja Iterasi: Iterable dan Iterator

Sepanjang buku ini, kamu sudah menggunakan loop for untuk melakukan iterasi pada list, string, dictionary, dan koleksi lainnya. Kamu sudah menulis kode seperti for item in my_list: berkali-kali. Tapi apa sebenarnya yang terjadi di balik layar saat Python mengeksekusi loop for? Bagaimana Python tahu cara melangkah melalui berbagai tipe koleksi?

Di bab ini, kita akan membahas protokol iterasi (iteration protocol) Python—mekanisme yang membuat loop for bekerja. Kamu akan belajar tentang iterable (iterables) (objek yang bisa kamu loop) dan iterator (iterators) (objek yang benar-benar melakukan proses melangkah melalui nilai). Memahami perbedaan ini akan memperdalam pengetahuanmu tentang cara kerja Python dan mempersiapkanmu untuk bekerja dengan generator di Bab 36.

35.1) Apa Artinya Sebuah Objek Bersifat Iterable

35.1.1) Konsep Iterabilitas

Sebuah iterable (iterable) adalah objek Python apa pun yang bisa diloop dengan loop for. Saat kita bilang "diloop," maksudnya Python bisa mengambil item dari objek tersebut satu per satu, secara berurutan.

Kamu sudah bekerja dengan banyak iterable:

python
# List bersifat iterable
numbers = [1, 2, 3, 4, 5]
for num in numbers:
    print(num)  # Output: 1, 2, 3, 4, 5 (pada baris terpisah)
 
# String bersifat iterable
text = "Python"
for char in text:
    print(char)  # Output: P, y, t, h, o, n (pada baris terpisah)
 
# Dictionary bersifat iterable (secara default pada key)
student = {"name": "Alice", "age": 20, "grade": "A"}
for key in student:
    print(key)  # Output: name, age, grade (pada baris terpisah)

Semua objek ini—list, string, dictionary, tuple, set, range, dan file—adalah iterable karena mereka mendukung protokol iterasi (iteration protocol) Python (seperangkat aturan yang memungkinkan Python melakukan loop pada mereka).

35.1.2) Apa yang Membuat Sebuah Objek Menjadi Iterable

Agar sebuah objek menjadi iterable, objek tersebut harus mengimplementasikan metode spesial bernama __iter__(). Metode ini mengembalikan sebuah objek iterator (iterator). Jangan khawatir soal detailnya dulu—kita akan membahas iterator di bagian berikutnya.

Kamu bisa mengecek apakah sebuah objek iterable dengan mencoba mendapatkan iterator darinya menggunakan fungsi bawaan iter():

python
# Menguji apakah objek bersifat iterable
numbers = [1, 2, 3]
iterator = iter(numbers)  # Berhasil - list bersifat iterable
print(type(iterator))  # Output: <class 'list_iterator'>
 
text = "Hello"
iterator = iter(text)  # Berhasil - string bersifat iterable
print(type(iterator))  # Output: <class 'str_iterator'>
 
# Mencoba dengan objek yang tidak iterable
value = 42
try:
    iterator = iter(value)  # Gagal - integer tidak iterable
except TypeError as e:
    print(f"Error: {e}")  # Output: Error: 'int' object is not iterable

Saat kamu memanggil iter() pada objek iterable, Python memanggil metode __iter__() milik objek tersebut dan mengembalikan sebuah iterator. Jika objek itu tidak memiliki metode ini, kamu akan mendapatkan TypeError.

35.1.3) Iterable vs Sequence

Penting untuk dipahami bahwa tidak semua iterable adalah sequence. Sequence adalah tipe iterable spesifik yang mendukung indexing dan punya urutan yang terdefinisi.

python
# Sequence mendukung indexing
my_list = [10, 20, 30]
print(my_list[0])  # Output: 10
 
my_string = "Python"
print(my_string[2])  # Output: t
 
# Set bersifat iterable tetapi BUKAN sequence (tanpa indexing, tanpa urutan yang dijamin)
my_set = {1, 2, 3}
for item in my_set:
    print(item)  # Berhasil - set bersifat iterable
 
# Tapi indexing tidak berfungsi
try:
    print(my_set[0])  # Gagal - set tidak mendukung indexing
except TypeError as e:
    print(f"Error: {e}")  # Output: Error: 'set' object is not subscriptable

Perbedaan kunci: Semua sequence (list, tuple, string, range) adalah iterable, tetapi tidak semua iterable adalah sequence. Set dan dictionary adalah iterable tetapi bukan sequence karena mereka tidak mendukung indexing.

Objek Python

Iterable

Bukan Iterable

Sequence

Iterable Non-Sequence

List, Tuple, String, Range

Set, Dictionary, File

Angka, None, Boolean

35.1.4) Kenapa Iterabilitas Itu Penting

Memahami iterabilitas membantu kamu:

  1. Tahu apa yang bisa kamu loop: Semua iterable bisa dipakai dengan loop for
  2. Memahami pesan error: "object is not iterable" berarti kamu tidak bisa memakainya dalam loop for
  3. Menggunakan comprehension: List, set, dan dictionary comprehension bekerja dengan iterable apa pun
  4. Bekerja dengan fungsi bawaan: Banyak built-in seperti sum(), max(), min(), dan sorted() menerima iterable apa pun
python
# Semua ini bekerja karena menerima iterable
numbers = [1, 2, 3, 4, 5]
print(sum(numbers))  # Output: 15
 
text = "Python"
print(max(text))  # Output: y (paling tinggi secara alfabet)
 
# Bahkan bisa bekerja dengan set
unique_values = {10, 5, 20, 15}
print(sorted(unique_values))  # Output: [5, 10, 15, 20]

35.2) Iterator Sehari-hari di Python (File, Range, Dictionary, dan Lainnya)

35.2.1) Apa Itu Iterator

Sebuah iterator (iterator) adalah objek yang merepresentasikan aliran data. Ia mengembalikan satu nilai setiap kali kamu meminta item berikutnya. Setelah iterator mengembalikan semua nilainya, iterator tersebut habis (exhausted) dan tidak bisa digunakan ulang.

Bayangkan iterator seperti bookmark di buku:

  • Ia mengingat kamu berada di posisi mana dalam urutan
  • Kamu bisa meminta item berikutnya
  • Begitu kamu mencapai akhir, kamu tidak bisa kembali tanpa membuat iterator baru

Perbedaan utama antara iterable dan iterator:

  • Iterable (iterable) adalah sesuatu yang bisa kamu iterasi (seperti list)
  • Iterator (iterator) adalah objek yang melakukan iterasi (mekanisme yang melangkah melalui list)
python
# List adalah sebuah iterable
numbers = [1, 2, 3]
 
# Mengambil iterator dari iterable
iterator = iter(numbers)
 
# Iterator adalah objek yang terpisah
print(type(numbers))    # Output: <class 'list'>
print(type(iterator))   # Output: <class 'list_iterator'>

35.2.2) Iterator dalam Loop for

Saat kamu menulis loop for, Python otomatis membuat iterator di balik layar:

python
numbers = [10, 20, 30]
 
# Yang kamu tulis:
for num in numbers:
    print(num)
 
# Yang Python lakukan secara internal (secara konseptual):
# 1. Memanggil iter(numbers) untuk mendapatkan iterator
# 2. Memanggil next() pada iterator secara berulang
# 3. Berhenti saat iterator melempar StopIteration

Berikut tampilan eksplisitnya:

python
numbers = [10, 20, 30]
 
# Iterasi manual (yang dilakukan for secara otomatis)
iterator = iter(numbers)
try:
    print(next(iterator))  # Output: 10
    print(next(iterator))  # Output: 20
    print(next(iterator))  # Output: 30
    print(next(iterator))  # Would raise StopIteration
except StopIteration:
    print("No more items")  # Output: No more items

Loop for menangani exception StopIteration secara otomatis, itulah kenapa kamu tidak pernah melihatnya dalam kode normal.

Ya

Tidak -> StopIteration

for item in iterable:

Python memanggil iter(iterable)

Mendapatkan objek iterator

Python memanggil next(iterator)

Masih ada item?

Assign ke item

Jalankan isi loop

Keluar dari loop

35.2.3) Objek File sebagai Iterator

Objek file adalah contoh iterator yang sangat bagus. Saat kamu melakukan iterasi pada file, ia membaca satu baris setiap kali:

python
# Membuat file contoh
with open("students.txt", "w") as file:
    file.write("Alice\n")
    file.write("Bob\n")
    file.write("Charlie\n")
 
# Membaca file baris demi baris
with open("students.txt", "r") as file:
    for line in file:
        print(line.strip())  # Output: Alice, Bob, Charlie (pada baris terpisah)

Objek file bersifat iterable sekaligus iterator. Mereka mengembalikan dirinya sendiri ketika kamu memanggil iter() pada mereka:

python
with open("students.txt", "r") as file:
    iterator = iter(file)
    print(file is iterator)  # Output: True (objek yang sama)
    
    # Membaca baris secara manual
    print(next(iterator))  # Output: Alice
    print(next(iterator))  # Output: Bob
    print(next(iterator))  # Output: Charlie

Ini hemat memori karena Python tidak memuat seluruh file ke memori—ia membaca satu baris setiap kali kamu memintanya.

35.2.4) Objek Range sebagai Iterator

Objek range adalah iterable yang menghasilkan angka sesuai permintaan:

python
# Range adalah sebuah iterable
numbers = range(1, 4)
print(type(numbers))  # Output: <class 'range'>
 
# Mengambil iterator dari range
iterator = iter(numbers)
print(type(iterator))  # Output: <class 'range_iterator'>
 
# Menggunakan iterator
print(next(iterator))  # Output: 1
print(next(iterator))  # Output: 2
print(next(iterator))  # Output: 3

Range hemat memori karena mereka tidak menyimpan semua angka di memori—mereka menghitung setiap angka saat diminta:

python
# Range ini merepresentasikan 1 juta angka tetapi memakai memori minimal
large_range = range(1000000)
print(type(large_range))  # Output: <class 'range'>
 
# Mengambil iterator
iterator = iter(large_range)
print(next(iterator))  # Output: 0
print(next(iterator))  # Output: 1
# ... bisa lanjut untuk 1 juta nilai

35.2.5) Iterator Dictionary

Dictionary menyediakan iterator berbeda untuk key, value, dan item:

python
student = {"name": "Alice", "age": 20, "grade": "A"}
 
# Iterasi pada key (default)
for key in student:
    print(key)  # Output: name, age, grade (pada baris terpisah)
 
# Mengambil iterator keys secara eksplisit
keys_iterator = iter(student.keys())
print(next(keys_iterator))  # Output: name
print(next(keys_iterator))  # Output: age
 
# Iterasi pada value
values_iterator = iter(student.values())
print(next(values_iterator))  # Output: Alice
print(next(values_iterator))  # Output: 20
 
# Iterasi pada item (pasangan key-value)
items_iterator = iter(student.items())
print(next(items_iterator))  # Output: ('name', 'Alice')
print(next(items_iterator))  # Output: ('age', 20)

35.2.6) Iterator Bisa Habis (Exhaustible)

Properti penting dari iterator adalah mereka hanya bisa dipakai sekali. Setelah habis, mereka tidak reset:

python
numbers = [1, 2, 3]
iterator = iter(numbers)
 
# Putaran pertama melalui iterator
print(next(iterator))  # Output: 1
print(next(iterator))  # Output: 2
print(next(iterator))  # Output: 3
 
# Iterator sekarang sudah habis
try:
    print(next(iterator))  # Raises StopIteration
except StopIteration:
    print("Iterator exhausted")  # Output: Iterator exhausted
 
# Untuk iterasi lagi, buat iterator baru
iterator = iter(numbers)
print(next(iterator))  # Output: 1 (mulai baru)

Ini berbeda dari iterable itu sendiri, yang bisa diiterasi berkali-kali:

python
numbers = [1, 2, 3]
 
# Iterasi pertama
for num in numbers:
    print(num)  # Output: 1, 2, 3
 
# Iterasi kedua (berhasil - membuat iterator baru)
for num in numbers:
    print(num)  # Output: 1, 2, 3

35.3) Menggunakan iter() dan next() untuk Melangkah Melalui Iterable

35.3.1) Fungsi iter()

Fungsi iter() menerima sebuah iterable dan mengembalikan sebuah iterator. Ini adalah langkah pertama dalam protokol iterasi:

python
# Membuat iterator dari berbagai iterable
numbers = [10, 20, 30]
iterator = iter(numbers)
print(type(iterator))  # Output: <class 'list_iterator'>
 
text = "Hi"
text_iterator = iter(text)
print(type(text_iterator))  # Output: <class 'str_iterator'>
 
my_set = {1, 2, 3}
set_iterator = iter(my_set)
print(type(set_iterator))  # Output: <class 'set_iterator'>

Setiap tipe iterable mengembalikan tipe iterator khususnya sendiri, tetapi semuanya bekerja dengan cara yang sama—kamu memanggil next() untuk mendapatkan nilai berikutnya.

35.3.2) Fungsi next()

Fungsi next() mengambil item berikutnya dari sebuah iterator. Saat tidak ada item lagi, ia melempar StopIteration:

python
colors = ["red", "green", "blue"]
iterator = iter(colors)
 
# Mengambil item satu per satu
print(next(iterator))  # Output: red
print(next(iterator))  # Output: green
print(next(iterator))  # Output: blue
 
# Tidak ada item lagi
try:
    print(next(iterator))  # Raises StopIteration
except StopIteration:
    print("No more colors")  # Output: No more colors

35.3.3) Memberikan Nilai Default pada next()

Kamu bisa memberikan nilai default ke next() sebagai argumen kedua. Saat iterator habis, alih-alih melempar exception StopIteration, next() akan mengembalikan nilai default yang kamu tentukan:

python
numbers = [1, 2, 3]
iterator = iter(numbers)
 
print(next(iterator))           # Output: 1
print(next(iterator))           # Output: 2
print(next(iterator))           # Output: 3
print(next(iterator, "Done"))   # Output: Done (default value, no exception)
print(next(iterator, "Done"))   # Output: Done (still exhausted)

Ini berguna saat kamu ingin menangani akhir iterasi secara mulus tanpa penanganan exception:

35.4) Membuat Iterator Kustom dengan iter dan next

35.4.1) Kenapa Membuat Iterator Kustom

Iterable bawaan Python (list, string, file) mencakup sebagian besar kasus umum. Namun, kadang kamu perlu membuat objek iterable-mu sendiri untuk perilaku yang lebih spesifik:

  • Menghasilkan urutan dengan logika kustom
  • Melakukan iterasi pada struktur data yang kamu desain
  • Membuat iterasi yang hemat memori untuk dataset besar
  • Menerapkan evaluasi malas (lazy evaluation) (menghitung nilai hanya saat dibutuhkan)

Membuat iterator kustom membutuhkan implementasi dua metode spesial: __iter__() dan __next__().

35.4.2) Protokol Iterator

Agar sebuah objek menjadi iterator, objek tersebut harus mengimplementasikan:

  1. __iter__(): Mengembalikan objek iterator itu sendiri (biasanya self)
  2. __next__(): Mengembalikan nilai berikutnya dalam urutan, atau melempar StopIteration saat selesai
python
class SimpleCounter:
    """Sebuah iterator yang menghitung dari start sampai end."""
    
    def __init__(self, start, end):
        self.current = start
        self.end = end
    
    def __iter__(self):
        """Kembalikan objek iterator (self)."""
        return self
    
    def __next__(self):
        """Kembalikan nilai berikutnya atau lempar StopIteration."""
        if self.current > self.end:
            raise StopIteration
        
        value = self.current
        self.current += 1
        return value
 
# Menggunakan iterator kustom
counter = SimpleCounter(1, 5)
 
for num in counter:
    print(num)
# Output: 1
# Output: 2
# Output: 3
# Output: 4
# Output: 5

Mari kita uraikan apa yang terjadi:

  1. Loop for memanggil iter(counter), yang memanggil counter.__iter__() dan mendapatkan kembali counter itu sendiri
  2. Loop berulang kali memanggil next(counter), yang memanggil counter.__next__()
  3. Setiap pemanggilan __next__() mengembalikan angka berikutnya dan menambah current
  4. Saat current > end, __next__() melempar StopIteration, dan loop berhenti

35.4.3) Penggunaan Manual Iterator Kustom

Kamu juga bisa menggunakan iterator kustom secara manual dengan iter() dan next():

python
counter = SimpleCounter(10, 13)
 
# Ambil iterator (mengembalikan dirinya sendiri)
iterator = iter(counter)
print(iterator is counter)  # Output: True
 
# Ambil nilai secara manual
print(next(iterator))  # Output: 10
print(next(iterator))  # Output: 11
print(next(iterator))  # Output: 12
print(next(iterator))  # Output: 13
 
# Sekarang sudah habis
try:
    print(next(iterator))
except StopIteration:
    print("Counter exhausted")  # Output: Counter exhausted

35.4.4) Iterator Bisa Habis (Dibahas Ulang)

Ingat bahwa iterator hanya bisa dipakai sekali:

python
counter = SimpleCounter(1, 3)
 
# Iterasi pertama
for num in counter:
    print(num)  # Output: 1, 2, 3
 
# Iterasi kedua (tidak berfungsi - iterator sudah habis)
for num in counter:
    print(num)  # Tidak mencetak apa pun - iterator sudah habis

Untuk iterasi lagi, kamu perlu membuat instance baru:

python
# Buat counter baru untuk setiap iterasi
for num in SimpleCounter(1, 3):
    print(num)  # Output: 1, 2, 3
 
for num in SimpleCounter(1, 3):
    print(num)  # Output: 1, 2, 3 (iterator baru)

35.4.5) Membuat Kelas Iterable (Bukan Hanya Iterator)

Seringnya, kamu ingin sebuah class yang iterable tetapi membuat iterator baru setiap kali. Untuk melakukan ini, pisahkan iterable dari iterator:

python
class CounterIterable:
    """Sebuah iterable yang membuat iterator counter baru."""
    
    def __init__(self, start, end):
        self.start = start
        self.end = end
    
    def __iter__(self):
        """Kembalikan iterator baru setiap kali."""
        return CounterIterator(self.start, self.end)
 
class CounterIterator:
    """Iterator sebenarnya yang melakukan proses menghitung."""
    
    def __init__(self, start, end):
        self.current = start
        self.end = end
    
    def __iter__(self):
        return self
    
    def __next__(self):
        if self.current > self.end:
            raise StopIteration
        value = self.current
        self.current += 1
        return value
 
# Sekarang kita bisa melakukan iterasi berkali-kali
counter = CounterIterable(1, 3)
 
# Iterasi pertama
for num in counter:
    print(num)  # Output: 1, 2, 3
 
# Iterasi kedua (berfungsi karena __iter__ membuat iterator baru)
for num in counter:
    print(num)  # Output: 1, 2, 3

Pola ini memisahkan tanggung jawab:

  • CounterIterable adalah iterable—ia tahu cara membuat iterator
  • CounterIterator adalah iterator—ia tahu cara melangkah melalui nilai

35.4.6) Contoh Praktis: Iterasi pada Struktur Data Kustom

Mari buat iterator untuk struktur data kustom—playlist sederhana:

python
class Playlist:
    """Playlist musik yang bisa diiterasi."""
    
    def __init__(self):
        self.songs = []
    
    def add_song(self, title, artist):
        """Menambahkan lagu ke playlist."""
        self.songs.append({"title": title, "artist": artist})
    
    def __iter__(self):
        """Kembalikan iterator untuk playlist."""
        return PlaylistIterator(self.songs)
 
class PlaylistIterator:
    """Iterator untuk melangkah melalui lagu-lagu dalam playlist."""
    
    def __init__(self, songs):
        self.songs = songs
        self.index = 0
    
    def __iter__(self):
        return self
    
    def __next__(self):
        if self.index >= len(self.songs):
            raise StopIteration
        
        song = self.songs[self.index]
        self.index += 1
        return song
 
# Menggunakan playlist
playlist = Playlist()
playlist.add_song("Imagine", "John Lennon")
playlist.add_song("Bohemian Rhapsody", "Queen")
playlist.add_song("Hotel California", "Eagles")
 
# Iterasi pada lagu
print("Now playing:")
for song in playlist:
    print(f"  {song['title']} by {song['artist']}")
# Output: Now playing:
# Output:   Imagine by John Lennon
# Output:   Bohemian Rhapsody by Queen
# Output:   Hotel California by Eagles
 
# Bisa diiterasi lagi (membuat iterator baru)
print("\nReplay:")
for song in playlist:
    print(f"  {song['title']}")
# Output: Replay:
# Output:   Imagine
# Output:   Bohemian Rhapsody
# Output:   Hotel California

35.4.7) Kapan Menggunakan Iterator Kustom

Buat iterator kustom ketika:

  1. Kamu butuh evaluasi malas (lazy evaluation): Menghasilkan nilai sesuai permintaan alih-alih menyimpan semuanya
  2. Kamu punya struktur data kustom: Jadikan iterable supaya bisa bekerja dengan loop for
  3. Kamu butuh logika iterasi khusus: Melewati item, mentransformasi nilai, atau menerapkan langkah yang kompleks
  4. Efisiensi memori penting: Menghasilkan urutan besar tanpa menyimpannya

Namun, di Bab 36, kamu akan belajar tentang generator (generators), yang menyediakan cara jauh lebih sederhana untuk membuat iterator menggunakan keyword yield. Generator biasanya lebih disukai dibanding mengimplementasikan __iter__() dan __next__() secara manual karena lebih ringkas dan lebih mudah dipahami.

Memahami cara membuat iterator kustom memberimu wawasan tentang bagaimana protokol iterasi Python bekerja, meskipun kamu seringnya akan memakai generator sebagai gantinya. Konsep yang kamu pelajari di sini—__iter__(), __next__(), dan StopIteration—bersifat fundamental untuk memahami generator dan teknik iterasi lanjutan lainnya di bab berikutnya.

© 2025. Primesoft Co., Ltd.
support@primesoft.ai