Iterator protocol in Python - Time & Space Complexity
Start learning this pattern below
Jump into concepts and practice - no test required
When using the iterator protocol in Python, it's important to know how the time to get each item grows as the collection gets bigger.
We want to understand how the number of steps changes when we loop through items using an iterator.
Analyze the time complexity of the following code snippet.
my_list = [1, 2, 3, 4, 5]
my_iter = iter(my_list)
while True:
try:
item = next(my_iter)
print(item)
except StopIteration:
break
This code uses the iterator protocol to get each item from a list one by one until all items are printed.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Calling
next()on the iterator to get the next item. - How many times: Once for each item in the list, until all items are retrieved.
Each call to next() takes about the same time, and we call it once per item.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | 10 calls to next() |
| 100 | 100 calls to next() |
| 1000 | 1000 calls to next() |
Pattern observation: The number of steps grows directly with the number of items. More items mean more calls.
Time Complexity: O(n)
This means the time to go through all items grows in a straight line with the number of items.
[X] Wrong: "Calling next() takes longer as we go further in the list."
[OK] Correct: Each next() call just moves to the next item, so it takes about the same time every time.
Understanding how iterators work and their time cost helps you explain how loops behave in Python, a skill useful in many coding situations.
"What if the iterator was over a linked list instead of a Python list? How would the time complexity change?"
Practice
__iter__ method do in the iterator protocol?Solution
Step 1: Understand the role of
The__iter____iter__method is called to get an iterator object from an iterable.Step 2: Identify what
It returns the iterator object itself, which has the__iter__returns__next__method to fetch items.Final Answer:
Returns the iterator object itself -> Option AQuick Check:
__iter__returns iterator object [OK]
- Confusing __iter__ with __next__
- Thinking __iter__ returns the next item
- Assuming __iter__ stops iteration
Solution
Step 1: Check required methods for iterator
An iterator class must have__iter__returning self and__next__to get next item.Step 2: Match methods with options
class MyIter: def __iter__(self): return self def __next__(self): pass correctly defines both__iter__and__next__methods.Final Answer:
Defines both __iter__ and __next__ methods -> Option CQuick Check:
Iterator class needs __iter__ and __next__ [OK]
- Using next() instead of __next__()
- Missing __iter__ method
- Defining iter() instead of __iter__()
class Count:
def __init__(self, limit):
self.limit = limit
self.num = 0
def __iter__(self):
return self
def __next__(self):
if self.num < self.limit:
self.num += 1
return self.num
else:
raise StopIteration
for i in Count(3):
print(i, end=' ')Solution
Step 1: Understand the iterator behavior
The Count class starts num at 0 and returns num+1 until it reaches limit 3.Step 2: Trace the loop output
Loop prints 1, 2, 3 then raises StopIteration to end loop.Final Answer:
1 2 3 -> Option DQuick Check:
Count(3) yields 1 to 3 [OK]
- Starting count from 0 instead of 1
- Expecting 4 as output
- Thinking StopIteration causes error
class MyIter:
def __init__(self):
self.data = [1, 2, 3]
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index <= len(self.data):
result = self.data[self.index]
self.index += 1
return result
else:
raise StopIterationSolution
Step 1: Analyze the index condition
Index goes from 0 to len(data)-1. Using <= allows index == len(data), causing IndexError.Step 2: Correct the condition
Change condition toself.index < len(self.data)to avoid out-of-range access.Final Answer:
The condition should be self.index < len(self.data) -> Option AQuick Check:
Index must be less than length to avoid error [OK]
- Using <= instead of < in index check
- Forgetting to return self in __iter__
- Starting index at 1 instead of 0
class EvenIterator:
def __init__(self, limit):
self.limit = limit
self.current = 0
def __iter__(self):
return self
def __next__(self):
while self.current < self.limit:
val = self.current
self.current += 1
if val % 2 == 0:
return val
raise StopIterationSolution
Step 1: Check iterator protocol methods
Class defines__iter__returning self and__next__with loop and StopIteration.Step 2: Verify filtering logic
Inside__next__, it loops until limit, returns only even values, skipping odds.Final Answer:
Correct implementation returning even numbers up to limit -> Option BQuick Check:
Iterator filters evens correctly [OK]
- Returning odd numbers by mistake
- Not raising StopIteration when done
- Returning new iterator in __iter__ each time
