Hackerrank Iterators and Generators — Questions and Answers
Question 1: What is the output of the following code? python def count_up(n): for i in range(n): yield i g = count_up(3) print(next(g)) print(next(g))
- 0 1 (Correct answer)
- 0 0
- 1 2
- 0 1 2
Correct answer: 0 1
count_up is a generator function. Each call to next() resumes execution until the next yield. The first next(g) yields 0; the second yields 1. Only two values are printed.
Question 2: Which of the following correctly implements the iterator protocol on a custom class?
- Define __iter__ returning self and __next__ returning the next value (Correct answer)
- Define __iter__ returning self and __yield__ returning the next value
- Define only __next__ returning the next value
- Define __iter__ returning a list of values
Correct answer: Define __iter__ returning self and __next__ returning the next value
The iterator protocol requires __iter__ (returns the iterator object, typically self) and __next__ (returns the next value or raises StopIteration). __yield__ is not a dunder method.
Question 3: What happens when a generator function reaches the end of its body without a yield?
- StopIteration is raised automatically (Correct answer)
- The generator returns None repeatedly
- A RuntimeError is raised
- The generator restarts from the beginning
Correct answer: StopIteration is raised automatically
When a generator function's body is exhausted (no more yield statements), Python automatically raises StopIteration, signalling to a for-loop or next() caller that iteration is complete.
Question 4: What is the difference between these two expressions? A: [x**2 for x in range(1000)] B: (x**2 for x in range(1000))
- A builds the full list in memory; B is a lazy generator that yields one value at a time (Correct answer)
- Both build lists but B is faster
- A is a tuple comprehension; B is a generator
- They are identical in behavior and memory use
Correct answer: A builds the full list in memory; B is a lazy generator that yields one value at a time
Square brackets produce a list comprehension, computing and storing all 1000 values immediately. Parentheses produce a generator expression, computing each value on demand without storing the whole sequence.
Question 5: What does the built-in iter() function do when called on a list?
- Returns a list_iterator object that supports __next__ (Correct answer)
- Returns a copy of the list
- Converts the list to a generator function
- Raises TypeError because lists are not iterable
Correct answer: Returns a list_iterator object that supports __next__
iter(obj) calls obj.__iter__() and returns the resulting iterator. For a list, it returns a list_iterator object. The list itself is iterable, but not an iterator — calling next() on the list directly would fail.
Question 6: What does the following generator produce? python def infinite_evens(): n = 0 while True: yield n n += 2 g = infinite_evens() print([next(g) for _ in range(4)])
- [0, 2, 4, 6] (Correct answer)
- [2, 4, 6, 8]
- [0, 2, 4]
- An infinite loop with no output
Correct answer: [0, 2, 4, 6]
The generator starts at n=0, yields 0, increments to 2, yields 2, etc. The list comprehension calls next() exactly 4 times, collecting [0, 2, 4, 6]. The infinite loop in the generator does not cause a problem because only 4 values are consumed.
What is the output of the following code?
python
def count_up(n):
for i in range(n):
yield i
g = count_up(3)
print(next(g))
print(next(g))