Closures, Iterators and Generators
Closures
No. We aren't talking about the programming language Clojure. A Closure is a function object that remembers values in enclosing scopes regardless of whether those scopes are still present in memory. If you have written a function that returned another function, you probably have used closures without even knowing.
Example:
def generate_power_func(n):
def nth_powah(x):
return x**n
return nth_powah
print generate_power_func(10)
# What happens?
Iterators
An iterator is just an object with a state that remembers where it is during iteration. Iterable objects can range from while loops to the actual iter object.
Example of iter function:
>>> x = iter([1, 2, 3, 4, 5, 6])
>>> print x
<listiterator object at 0x02B2AB30>
x.next()
1
x.next()
2
x.next()
3
x.next()
4
x.next()
5
x.next()
6
x.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
The one we will be using the most is the xrange() function. Below is an iterator, implemented as a class that works like xrange. This is your first major look at classes... don't freak out! Let's break this down.
class yrange:
def __init__(self, n):
self.i = 0
self.n = n
def __iter__(self):
return self
def next(self):
if self.i < self.n:
i = self.i
self.i += 1
return i
else:
raise StopIteration()
# using y range
yrange(3)
y.next()
0
y.next()
1
y.next()
2
y.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 14, in next
StopIteration
As we learned in the Data Types lecture... xrange() loads one at a time. The yrange() class here does the same thing. There is lists, tuples, etc loaded up. Instead, we deal with two variables... i and n.
Generators
Generators are also iterators (iterators are not always generators though). Generators simplify the creation of iterators. A generator is simply a function that produces a sequence of values to iterate through rather than a single value.
def yrange(n):
i = 0
while i < n:
yield i
i += 1
my_gen = yrange(10)
print my_gen
for i in my_gen:
print(i)
# output
<generator object yrange at 0x007F3328>
0
1
2
3
4
5
6
7
8
9
Taking a look at the code above... we created a sequence object and iterated over it using a for loop.
Note: When functions return all state is lost, a yield preserves local state and returns a generator object.