``````

In [5]:

class ThingIterator(object):
def __init__(self, thing):
self.thing = thing
self.index = 0

def __iter__(self):
return self

def next(self):
if self.index >= len(self.thing.data):
raise StopIteration

retval = self.thing.data[self.index]
self.index += 1
return retval ** 2

# python3 way
__next__ = next

class Thing(object):
def __init__(self, data):
self.data = data

def __iter__(self):
return ThingIterator(self)

def iter_squares(self):
return ThingIterator(self)

def iter_squares(x):
return x.iter_squares()

t = Thing([1, 2, 3])
for x in t.iter_squares():
print(x)

``````
``````

1
4
9

``````

Los generadores son funciones que se comportan como iteradores, evitando al programador escribir el código de soporte que hemos visto en la sección anterior. Esto es gracias a la palabra reservada `yield` que permite al generador emitir un valor sin terminar.

``````

In [24]:

def more():
yield 20
yield 300
yield 4000

for x in more():
print(x)

``````
``````

20
300
4000

``````
``````

In [7]:

def get_squares(seq):
for x in seq:
yield x ** 2

for x in get_squares([1, 2, 3]):
print(x)

``````
``````

1
4
9

``````
``````

In [9]:

for x in get_squares(xrange(1, 4)):
print(x)

``````
``````

1
4
9

``````
``````

In [14]:

gen = get_squares([1, 2, 3])
print(gen)
print(gen.next())
print(gen.next())

``````
``````

<generator object get_squares at 0x7f4db07f54b0>
1
4

``````

### generator expressions

Son equivalentes la comprensión de listas para generadores.

``````

In [16]:

gen = (x ** 2 for x in [1, 2, 3])
print(gen)

``````
``````

<generator object <genexpr> at 0x7f4db07f54b0>

``````
``````

In [19]:

sum(x**2 for x in xrange(100) if x % 2)

``````
``````

Out[19]:

166650

``````

## `itertools`

``````

In [4]:

import itertools as it

``````
``````

In [5]:

x = it.count(5, 3)
print(x.next())
print(x.next())
print(x.next())
print(x.next())

``````
``````

5
8
11
14

``````
``````

In [6]:

x = it.cycle([2, 4, 8])
print(x.next())
print(x.next())
print(x.next())
print(x.next())

``````
``````

2
4
8
2

``````
``````

In [7]:

for x in it.repeat("a", 5):
print(x)

``````
``````

a
a
a
a
a

``````
``````

In [8]:

for x in it.chain("foo", "bar", "bizz"):
print(x)

``````
``````

f
o
o
b
a
r
b
i
z
z

``````

`imap()` e `ifilter()` son versiones lazy de `map()` y `filter()`.

``````

In [9]:

for x in it.takewhile(lambda x: x<5, [1,4,6,4,1]):
print(x)

``````
``````

1
4

``````
``````

In [12]:

for x in it.dropwhile(lambda x: x<5, [1,4,6,4,1]):
print(x)

``````
``````

6
4
1

``````
``````

In [16]:

print([str.join('', list(x)) for x in it.product('ABCD', repeat=2)])

``````
``````

['AA', 'AB', 'AC', 'AD', 'BA', 'BB', 'BC', 'BD', 'CA', 'CB', 'CC', 'CD', 'DA', 'DB', 'DC', 'DD']

``````
``````

In [17]:

for i,j in it.product(range(5), repeat=2):
print(i, j)

``````
``````

(0, 0)
(0, 1)
(0, 2)
(0, 3)
(0, 4)
(1, 0)
(1, 1)
(1, 2)
(1, 3)
(1, 4)
(2, 0)
(2, 1)
(2, 2)
(2, 3)
(2, 4)
(3, 0)
(3, 1)
(3, 2)
(3, 3)
(3, 4)
(4, 0)
(4, 1)
(4, 2)
(4, 3)
(4, 4)

``````
``````

In [ ]:

``````