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 yieldgenerator

    • 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