Iterators
Contents
Iterators#
BB1000 Programming in Python
layout: false
Iterables, iterators and generators#
Examples#
objects that can be used in for loops
A list#
>>> li = [0, 1, 2]
>>> for i in li:
... print(i, end=" ")
0 1 2
Dictionary#
The loop variable is the key of the key-value pair
>>> dict = {'a':1, 'b':2}
>>> for k in dict:
... print(k, dict[k])
a 1
b 2
String#
>>> str = 'abc'
>>> for c in str:
... print(c)
a
b
c
File objects#
#123.txt
one
two
three
>>> for row in open('123.txt'):
... print(row, end="")
one
two
three
Iterable vs iterator#
>>> li = [0, 1, 2]
>>> print(li)
[0, 1, 2]
If you can call iter
with an object that object it is iterable
>>> li_iter = iter(li)
>>> type(li_iter)
<class 'list_iterator'>
iter
will return an object that supports next
- an iterator
>>> next(li_iter)
0
>>> next(li_iter)
1
>>> next(li_iter)
2
>>> next(li_iter)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
Illustrates what happens behind the scenes in a for loop
Objects supporting iteration#
What does iter vs next (built-in function) do? Roughly it delegates
#What the builtin iter roughly does
def iter(object):
return object.__iter__()
#What the builtin next roughly does
def next(object):
return object.__next__()
iterables are defined by classes with an
__iter__
method, which which should return an iteratoriterators are defined by classes with a
__next__
method, producing the next value of the sequencethe for loop will stop when a
StopIteration
exception is encountered
Defining your own iterator#
class Counter:
def __init__(self, size):
print("__init__:", size)
self.size = size
self.start = 0
def __iter__(self):
print("__iter__:", self.size)
return CounterIter(self.start, self.size)
class CounterIter:
def __init__(self, start, size):
self.start = start
self.size = size
def __next__(self):
if self.start < self.size:
self.start = self.start + 1
return self.start
raise StopIteration
>>> c = Counter(3)
__init__: 3
>>> for num in c:
... print(num, end=" ")
__iter__: 3
1 2 3
Generators#
Functions that contain the yield statment
Support iteration protocol - they return an iterator
Resumable
Generators are not executed when invoked, they are iterated over
function vs. generator#
>>> def f(n):
... return n
>>> type(f)
<class 'function'>
>>> type(f(1))
<class 'int'>
>>> def g(n):
... yield n
>>> type(g)
<class 'function'>
>>> type(g(1))
<class 'generator'>
Example#
>>> def g(n):
... print('enter g with',n)
... yield n
... print('after yield')
>>> g2=g(2)
>>> next(g2)
enter g with 2
2
>>> next(g2)
after yield
Traceback (most recent call last):
...
StopIteration
So: this function appears to pause at the yield statement after returning the value and continue from there the next time the next() method is called…
When the function exits a StopIteration exception is raised
Example#
>>> def g(n):
... print('enter g with ',n)
... i=0
... while i < n:
... yield i
... print('after yield')
... i += 1
... print('after while')
>>> g2=g(2)
>>> next(g2)
enter g with 2
0
>>> next(g2)
after yield
1
>>> g2.next()
after yield
after while
Traceback (most recent call last):
File "gen.py", line 42, in <module>
print(g2.next())
StopIteration
Iterator in for loop#
>>> for i in g(5):
... print(i, end=" ")
enter g with 5
0 after yield
1 after yield
2 after yield
3 after yield
4 after yield
after while
Example: fibonacci#
>>> def fib(n):
... a = 1
... b = 2
... while a < n:
... yield a
... a, b = b, a + b
>>> for i in fib(5):
... print(i, end=" ")
1 2 3
Convert to list#
It is always possible to convert a generator to a list
>>> list(fib(100))
[1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
Summary#
Several common types support iteration (list, dict, file, str)
Objects that support iteration have an
__iter__
method returning an iteratorThe iterators have a
[__next__](__next__)
method that steps through some sequenceGenerators are functions with a
yield
statement and work like iterators