Iterators and Generators
Iterators and Generators
and Generators, covering Iterable vs. Iterator, Custom Iterators, and the
yield Keyword and Generator Expressions. This script is tailored for a
beginner-to-intermediate audience, designed for a video course with an
engaging, clear tone, and packed with explanations, examples, practical
applications, and pro tips to make it an outstanding resource.
python
my_list = [1, 2, 3] # Iterable
for x in my_list:
print(x)
(Output: 1
2
3)
"my_list is an iterable—for loops work because it has a hidden trick. Under the
hood, Python calls iter() to get an iterator:
(Type and run)
python
my_iter = iter(my_list) # Iterator
print(next(my_iter))
print(next(my_iter))
print(next(my_iter))
(Output: 1
2
3)
"iter() turns the iterable into an iterator, and next() fetches items one by one. Try
one more next():
(Type and run)
python
print(next(my_iter)) # StopIteration!
"Once it’s done, it raises StopIteration—that’s how for loops know to stop.
Iterables provide data; iterators deliver it step-by-step. Next, let’s make our
own!"
python
class Countdown:
def __init__(self, start):
self.current = start
def __iter__(self):
return self
def __next__(self):
if self.current < 0:
raise StopIteration
value = self.current
self.current -= 1
return value
"Let’s test it:
(Type and run)
python
count = Countdown(3)
for num in count:
print(num)
(Output: 3
2
1
0)
"When current hits -1, StopIteration stops the loop. You can also use next()
manually:
(Type and run)
python
count = Countdown(2)
print(next(count))
print(next(count))
print(next(count))
(Output: 2
1
0)
"Custom iterators let you define any sequence—like Fibonacci or even random
numbers. They’re powerful for tailored iteration!"
python
def countdown(start):
current = start
while current >= 0:
yield current
current -= 1
"Let’s use it:
(Type and run)
python
for num in countdown(3):
print(num)
(Output: 3
2
1
0)
"Each yield sends a value and pauses until the next request. Check this with
next():
(Type and run)
python
gen = countdown(2)
print(next(gen))
print(next(gen))
print(next(gen))
(Output: 2
1
0)
"Generators are lazy—they compute values only when needed, unlike lists that
store everything. Let’s generate squares:
(Type and run)
python
def squares(limit):
for i in range(limit):
yield i ** 2
print(list(squares(5)))
(Output: [0, 1, 4, 9, 16])
"Memory-efficient and elegant—generators rock!"
python
list_comp = [x**2 for x in range(5)] # List
gen_exp = (x**2 for x in range(5)) # Generator
print(list_comp)
print(list(gen_exp))
(Output: [0, 1, 4, 9, 16]
[0, 1, 4, 9, 16])
"The list stores all values; the generator yields them one by one. Use it in a loop:
(Type and run)
python
gen = (x**2 for x in range(5))
for square in gen:
print(square)
(Output: 0
1
4
9
16)
"Add conditions, like even numbers:
(Type and run)
python
evens = (x for x in range(10) if x % 2 == 0)
print(list(evens))
(Output: [0, 2, 4, 6, 8])
"Generator expressions are perfect for big data—lazy evaluation saves memory!"
python
def read_lines(filename):
with open(filename, 'r') as file:
for line in file:
yield line.strip()
# Assume 'data.txt' has: apple, banana, cherry
for line in read_lines('data.txt'):
print(line)
(Output: apple
banana
cherry)
"Next, a custom iterator for odd numbers:
(Type and run)
python
class OddNumbers:
def __init__(self, limit):
self.limit = limit
self.current = -1
def __iter__(self):
return self
def __next__(self):
self.current += 2
if self.current > self.limit:
raise StopIteration
return self.current
odds = OddNumbers(5)
print(list(odds))
(Output: [1, 3, 5])
"Finally, a generator expression for primes:
(Type and run)
python
def is_prime(n):
return n > 1 and all(n % i != 0 for i in range(2, n))
primes = (x for x in range(20) if is_prime(x))
print(list(primes))
(Output: [2, 3, 5, 7, 11, 13, 17, 19])
"These show iterators and generators in action!"
python
names = ['Alice', 'Bob']
scores = [85, 90]
pairs = ((name, score) for name, score in zip(names, scores))
print(list(pairs))
(Output: [('Alice', 85), ('Bob', 90)])
"Keep it simple and efficient!"