# Iteration, Comprehensions, and Generators¶

## Coverage Of This Topic¶

• List comprehensions

• The `range` function

• Writing generators using `yield`

• Generator expressions

## `for` Recap: Sequential Datatypes¶

 ```for i in [1,2,3,4]: print(i) ``` ```\$ python code/10-for-recap.py 1 2 3 4 ``` This is the point in sequential datatypes: iteration with `for`, mainly

## Primitive Transformation `[1,2,3,4]` ⟶ `[1,4,9,16]`¶

 ```def squares(seq): ret = [] for i in seq: ret.append(i**2) return ret for i in squares([1,2,3,4]): print(i) ``` ```\$ python code/20-squares-func.py 1 4 9 16 ``` Define function to create squares list

## List Comprehension¶

Problem:

• function used in one place

• … but defined elsewhere

 ```for i in [i**2 for i in [1,2,3,4]]: print(i) ``` ```\$ python code/30-squares-comprehension.py 1 4 9 16 ``` Same behavior as `squares()`, but more readable Provided that your colleagues know what list comprehensions are

Where does readability end?`if` clause

 ```for i in [i**2 for i in [1,2,3,4,5,6,7,8,9] if i%2 != 0]: print(i) ``` ```\$ python code/40-odd-squares-comprehension.py 1 9 25 49 81 ``` Somewhere a line has to be drawn ⟶ it’s a team decision, as everything

## Larger Input List (Still Allocated)¶

Problem: what about large lists? Say, 10 million elements.

 ```inputlist = [] print('filling input list') cnt = 0 while cnt < 10000000: inputlist.append(cnt) cnt += 1 print('calculating squares') for i in [i**2 for i in inputlist]: print(i) ``` ```\$ python code/50-larger-list.py filling input list calculating squares ... numbers ... ``` Start without the `print()` statements. Wonder why it takes so long. * Composing two lists Burden on OS

## Input List ⟶ `range`¶

Problem: using lists primarily for iteration. Can this be done

better?

 ```print('creating input range') inputrange = range(10000000) print('calculating squares') for i in [i**2 for i in inputrange]: print(i) ``` ```\$ python code/60-input-range.py creating input range calculating squares ... numbers ... ``` Morph `inputlist` into `inputrange` eliminate loop altogether * `range` has been created in no time Discuss `range` ⟶ Generator

## Output List ⟶ `yield`¶

Problem: still using temporary list in `squares()`

 ```def squares(seq): for i in seq: yield i**2 for i in squares(range(1,5)): print(i) ``` ```\$ python code/70-output-yield.py 1 4 9 16 ``` Morph back to `squares()` function What’s so disgusting? Temporary list in `squares()` Gently introduce `yield`: remove temporary list + its return What’s still disgusting? ⟶ input list ⟶ `range` Perfect! Discuss `yield` Still `def`, but contains `yield` ⟶ generator Morphing temporary lists to `yield` is straightforward

## Generator Function ⟶ Generator Expression¶

Problem (re-introduced): `squares()` definition and usage are

too far apart

 ```for i in (i**2 for i in range(1,5)): print(i) ``` ```\$ python code/80-generator-expression.py 1 4 9 16 ``` Generator expression ⟶ just like list comprehension, only smarter