Concepts
- What method(s) must iterables have?
- What method(s) must iterators have?
- What data type does
__iter__return? - What data type does
__next__return? - Is a generator an iterable or an iterator?
- What keyword in the body of a function makes that function return a generator object?
- How is
yielddifferent fromreturn? - When you call
nexton a generator, the body starts executing at what line? At what line will it stop? At what line will it start the next time you callnext? - What happens when you call
liston an iterable or an iterator? What happens if you call it a second time on the same objects? - Can you iterate through an iterable in a for loop? Can you iterate through an iterator in a for loop?
__iter__
__iter__AND__next__
iterator
whatever type each element of the sequence is
iterator
yield
yielddoes not close the frame.yieldoutputs a value, and keeps the frame open untilStopIterationis raised.
execution starts at the first line of the body and stops after the line withyieldis executed. whennextis called again, execution picks up at the line right after theyieldstatement (where it last left off) and stops after anotheryieldexecution or if there are no more lines in the body.
when you callliston an iterable or an iterator, Python callsiteron it and then attempts to construct a list with thenextelement untilStopIterationis reached, then returns the constructed list.
you can use both iterables and iterators in a for loop.
What would Python print?
The following classes define an iterable representing the sequence of multiples for any given number and the iterator that returns the next value in the sequence. The sequence goes up to 1000.
class Multiples:
def __init__(self, num):
self.num = num
def __iter__(self):
return MultiplesIterator(self.num)
class MultiplesIterator:
def __init__(self, num):
self.num = num
self.curr = num
def __iter__(self):
return self
def __next__(self):
if self.curr >= 1000:
raise StopIteration
val = self.curr
self.curr = self.curr + self.num
return valWhat will the following lines output?
>>> hundreds = Multiples(100)
>>> next(hundreds)
TypeError: 'Multiples' object is not an iterator
>>> next(iter(hundreds))
100
>>> next(iter(hundreds))
100
>>> i = iter(hundreds)
>>> i is iter(hundreds)
False
>>> i is iter(i)
True
>>> next(i)
100
>>> next(i)
200
>>> list(hundreds)
[100, 200, 300, 400, 500, 600, 700, 800, 900]
>>> list(i)
[300, 400, 500, 600, 700, 800, 900]
>>> list(hundreds)
[100, 200, 300, 400, 500, 600, 700, 800, 900]
>>> list(i)
[]
>>> for i in hundreds:
... print(i)
5
25
125
625
>>> for x in i:
... print(x)
Explanation: Multiples's __iter__ method always returns a *new instance* of MultiplesIterator, whereas MultiplesIterator's __iter__ simply returns itself. For iterables, calling iter will reset the iterator, but since iterators just return themselves, they do not reset.
TOGGLE SOLUTION TOGGLE EXPLANATION
Writing code
Fill in the following definition of a generator function which yields every number from 1 to n and prints 'm was a factor' if the previous number, m, was a factor of n. See the doctests for an example.
def print_factor(n):
"""
>>> gen = print_factor(8)
>>> next(gen)
1
>>> next(gen)
1 was a factor
2
>>> next(gen)
2 was a factor
3
>>> next(gen)
4
>>> next(gen)
4 was a factor
5
"""
"*** YOUR CODE HERE ***"def print_factor(n):
curr = 1
while curr <= n:
yield curr
if n % curr == 0:
print(str(curr) + " was a factor")
curr += 1