Python Basics: Iterators & Generators
Learn to create memory-efficient sequences and control iteration like a pro!
1. Iterators: Objects That Remember Their State
An iterator is an object that produces values one at a time using __iter__()
and __next__()
.
How Iterators Work
__iter__()
: Returns the iterator object itself.__next__()
: Returns the next value. RaisesStopIteration
when done.
Example: Create a custom counter iterator.
class Counter:
def __init__(self, limit):
self.limit = limit
self.current = 0
def __iter__(self):
return self
def __next__(self):
if self.current < self.limit:
num = self.current
self.current += 1
return num
else:
raise StopIteration
# Use the iterator
counter = Counter(3)
for num in counter:
print(num)
Output:
0
1
2
2. Generators: Simpler Iterators with yield
A generator is a function that produces values on-the-fly using yield
. It remembers its state between calls.
Example: Number Generator
def number_generator(limit):
num = 0
while num < limit:
yield num # Pauses here and returns num
num += 1
# Use the generator
gen = number_generator(3)
print(next(gen)) # Output: 0
print(next(gen)) # Output: 1
print(next(gen)) # Output: 2
Key Advantages:
- Memory-efficient: Generates values on demand.
- Simpler syntax: No need for
__iter__()
or__next__()
.
3. Real-World Use Cases
Use Case 1: Large File Processing
Generator to read a huge file line-by-line without loading it entirely into memory:
def read_large_file(file_path):
with open(file_path, "r") as file:
for line in file:
yield line.strip()
# Process 1GB log file
for line in read_large_file("logs.txt"):
if "ERROR" in line:
print(line)
Use Case 2: Infinite Sequence
Generator for an infinite Fibonacci sequence:
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
fib = fibonacci()
print(next(fib)) # 0
print(next(fib)) # 1
print(next(fib)) # 1
print(next(fib)) # 2
Key Differences
Feature | Iterator | Generator |
---|---|---|
Syntax | Class with __iter__/__next__ . |
Function with yield . |
Memory Use | Can store all values. | Generates values on-the-fly. |
Complexity | More code. | Less code, easier to write. |
Common Mistakes
- ❌ Forgetting
raise StopIteration
: Causes infinite loops in custom iterators. - ❌ Reusing exhausted generators: Generators can’t reset – create a new instance.
- ❌ Overusing generators: Use lists for small, reusable datasets.
Practice Quiz
What’s the output?
def countdown(n):
while n > 0:
yield n
n -= 1
for num in countdown(2):
print(num)
Answer:
2
1
Fix the iterator:
class MyRange:
def __init__(self, start, end):
self.start = start
self.end = end
def __next__(self):
if self.start < self.end:
current = self.start
self.start += 1
return current
else:
raise StopIteration
Missing: The __iter__()
method. Add def __iter__(self): return self
.
Fun Activity: Password Generator
import random
import string
def password_generator(length=8):
chars = string.ascii_letters + string.digits
while True:
password = ''.join(random.choice(chars) for _ in range(length))
yield password
passwords = password_generator()
print(next(passwords)) # e.g., "A3b9C2dE"
Key Takeaways
- ✅ Iterators: Use
__iter__()
and__next__()
for custom sequences. - ✅ Generators: Simplify iteration with
yield
and pause/resume functionality. - ✅ Use Cases: Process large files, infinite sequences, or memory-heavy data.
What’s Next?
Learn decorators to modify or enhance functions and classes!
Tags:
python