for Loops#


Questions:#

  • How can I make a program do things repeatedly?

Learning Objectives:#

  • Explain what for loops are normally used for.

  • Trace the execution of a simple (unnested) loop and correctly state the values of variables in each iteration.

  • Write for loops that use the Accumulator pattern to aggregate values.


A for loop executes commands once for each value in a collection#

  • Doing calculations on the values in a list one by one is as painful as working with many individual variables rather than a list or dictionary (e.g., life_exp_1900, life_exp_1920, etc. rather than life_exp = [48.1, 56.6, 64.0, 71.0, 75.2, 79.2]

  • A for loop tells Python to execute some statements once for each value in a list, a character string, or some other collection

  • The for command means, “for each thing in this group, do these operations”

for number in [2, 3, 5]:
    print(number)

The for loop above is equivalent to:

print(2)
print(3)
print(5)

and the for loop’s output is:

for number in [2, 3, 5]:
    print(number)
2
3
5
  • However, the for loop is scalable to any length input.

  • This makes it flexible, because we might not know in advance how many inputs we want to operate on.

A for loop is made up of a collection, a loop variable, and a body.#

In our example:

for number in [2, 3, 5]:
    print(number)
  • The collection, [2, 3, 5], is what the loop is being run on.

  • The loop variable, number, is what changes for each iteration of the loop.

    • This keeps track of where we are in the loop — the “current thing”

  • The body, print(number), specifies what to do for each value in the collection.

The first line of the for loop must end with a colon, and the body must be indented#

  • The colon at the end of the first line signals the start of a block of statements.

  • Python uses indentation to show nesting (this is different from other languages, which often use explicit markers for the start and end (e.g., {} or begin/end) of a nesting.

    • Any consistent indentation is legal, but almost everyone uses four spaces.

for number in [2, 3, 5]:
    print(number)

Indentation is always meaningful in Python#

  • After a for statement, Python expects at least one indented line with the body of the for loop

  • The end of the body of a for loop is indicated by a line of code that is not indented

  • It’s good coding style (although not required) to put a blank line after the end of the body of a for loop, before the next line of code that’s not in the for loop

For example:

for country in ['Canada', 'USA', 'Mexico']:
    print(country)
    
print('All done printing country names')
Canada
USA
Mexico
All done printing country names
  • Because indentation is always meaningful, the code below generates an error:

life_exp_1900 = 48.1
  life_exp_1920 = 56.6
life_exp_1900 = 48.1
life_exp_1920 = 56.6

This error can be fixed by removing the extra spaces at the beginning of the second line.

life_exp_1900 = 48.1
life_exp_1920 = 56.6

Question#

Is an indentation error a syntax error or a runtime error?

Solution#

A Python IndentationError is a syntax error. Programs with syntax errors cannot be started. A program with a runtime error will start but an error will be thrown under certain conditions.

Loop variables can be called anything.#

As with all variables, loop variables are:

  • Created on demand

  • Meaningless: their names can be anything at all

  • So the following are valid for loops, but not great loop variable names (unless in the first example your data actually concern kittens):

for kitten in [2, 3, 5]:
    print(kitten)
2
3
5
for ytrjmn in [2, 3, 5]:
    print(ytrjmn)   
2
3
5
  • As always, clear and meaningful variable names are best practice

The collection can be a list, dictionary, etc. that was defined previously#

life_exp = [48.1, 56.6, 64.0, 71.0, 75.2, 79.2]

for e in life_exp:
    print(e)
48.1
56.6
64.0
71.0
75.2
79.2
  • Strings can also be used as the collection, in which case the loop will step through each character of the string:

for c in 'SURGE':
    print(c)
S
U
R
G
E

The body of a loop can contain many statements.#

primes = [2, 3, 5]

for p in primes:
    squared = p ** 2
    cubed = p ** 3
    print(p, squared, cubed)
2 4 8
3 9 27
5 25 125

Use range to iterate over a sequence of numbers#

  • This is an easy way to generate a sequence of numbers to make looping over large ranges more efficient

  • range(n) is the numbers 0 … n-1

    • recall that Python counts from zero

    • as with slicing, range() goes up to, but does not include, the last value

for i in range(10):
    print(i)
0
1
2
3
4
5
6
7
8
9
  • If we provide two arguments to range(), the first is the start and the second is the end of the range:

for i in range(10, 15):
    print(i)
10
11
12
13
14
  • Although the range() function can substitute for typing out a list, it does not actually produce a list

  • The numbers are produced on demand to make looping over large ranges more efficient. We can see this by printing the result of calling the range() function:

print(range(10))
range(0, 10)

…or by printing its type:

print(type(range(10)))
<class 'range'>

The “accumulator” pattern turns many values into one#

A common pattern in programs is to:

  1. Initialize an accumulator variable to zero

  2. Update the variable with values from a collection.

  • The code below sums the integers 1-10:

total = 0

for number in range(1, 11):
    total = total + number
    
print(total)
55
  • Note that we specify a range of 1-11 because the range() goes up to, but does not include, the ‘end’ number

  • Read total = total + number as:

    • Add the value of number to the current value of the accumulator variable total.

    • Assign that to total, replacing the current value.

Accumulators can also be empty lists#

This is where empty lists and the .append() method can be really useful - we can initialize an empty list and the append to it each time through the loop:

output = []
for i in range(10):
    output.append(i * 2)
    
print(output)
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

List comprehension#

  • List comprehension is a special kind of for loop that you can use to create a list

  • This can be a very powerful and compact way of doing things

  • For example, the following code creates a list of values from 1 to 10 in a single line of code:

[x for x in range(1, 11)]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  • List comprehension can be very useful if you want to apply some operation to every value in a list

  • For example, let’s say we had a set of times in seconds, and we wanted to convert them to milliseconds (thousandths of a second):

time_sec = [0.19859864, 1.35544082,  0.81298099,  1.80873061,  0.78908326, 
            1.40242708, 0.39563692,  1.91673302, 1.07524985, 1.02169021]

time_ms = [t * 1000 for t in time_sec]

print(time_ms)
[198.59864, 1355.44082, 812.98099, 1808.73061, 789.08326, 1402.4270800000002, 395.63692, 1916.73302, 1075.2498500000002, 1021.6902100000001]

The equivalent for loop would require three lines of code:

time_ms = []

for t in time_sec:
    time_ms.append(t * 1000)
    
print(time_ms)    
[198.59864, 1355.44082, 812.98099, 1808.73061, 789.08326, 1402.4270800000002, 395.63692, 1916.73302, 1075.2498500000002, 1021.6902100000001]

We can put multiple operations in a list comprehension, for example to also round our milliseconds down to whole numbers:

time_ms = [round(t * 1000) for t in time_sec]

print(time_ms)
[199, 1355, 813, 1809, 789, 1402, 396, 1917, 1075, 1022]

Lambda Functions and Mapping#

Yet another way of performing an operation over a collection, is by combining map() and lambda functions. Recall that functions are Python commands like print() and type(). A Python function is actually a program that is run when you execute the function.

lambda functions are a way of creating your own very small functions, on the fly, to perform a specific operation or combination of operations. Lambda functions take variables as input and return a value (the results of the operations it performs). A lambda function is composed of:

  • the statement lambda

  • the argument(s) — the variable(s) you pass to the operations in the lambda function

  • a colon

  • the expression (operations you want the function to perform on the arguments)

For example, we-writing the above code to convert seconds to milliseconds and round to integers as a lambda function would be:

lambda t: round(t * 1000)

Mapping#

As with the print() or round() functions, with a lambda function you need to pass something to it as an argument for it to operate on. The variable t in the lambda function above represents the argument passed to the lambda function. We can pass a collection to a lambda function, so that it will be applied to each item in the collection.

The map() function is used to apply a lambda function to a collection, instead of using a for loop

map() takes two arguments:

  • the function you want to apply

  • the collection you want to apply it to

The code below maps the convert-to-milliseconds-and-round function to the time_sec list:

time_ms = map(lambda t: round(t * 1000), 
              time_sec)

However, the result is not a list, but a Python `map’ object:

print(type(time_ms))
print(time_ms)
<class 'map'>
<map object at 0x1057adab0>

This is because the result of the map() function is a mapping between the lambda function and the collection. That is, it specifies how a function is applied to the collection, but it doesn’t actually apply the function until you ask it to generate output. We can do this by telling Python to format the result of the mapping as a list with the list() function:

time_ms = list(map(lambda t: round(t * 1000), 
                   time_sec))

print(time_ms)
[199, 1355, 813, 1809, 789, 1402, 396, 1917, 1075, 1022]

Which to use?#

  • for loops are the most general, multipurpose way of performing an operation over many items

  • for loops are quite explicit compared to list comprehensions or mapped lambda functions, and a good way to start tackling any problem

  • The body of for loops can be of any length, whereas list comprehensions and lambda functions are best used with only one or a few operations

  • List comprehensions result in lists, which is not always what you want out of a for loop

  • List comprehensions are generally faster than for loops, for creating lists

  • Lambda functions will become useful later in the course, as we start working with larger data sets. Stay tuned!


Exercises#

Tracing Execution#

In the Markdown cell below (not code!) trace the execution of the for loop as it would run. That is, type what you expect would be the values of total and c each time through the loop. Use a separate line for each pass through the loop.

total = 0
for c in 'tin':
    total = total + 1

An interesting thing to note in this example is that we never use the looping variable (c) inside the loop; we only use it to control how many times we go through the loop. It’s a bit odd to use a string to define the number of times we want to perform an operation (since we’re not operating on the string), but it’s entirely valid Python code.

Reversing a String#

Fill in the blanks in the program below so that it reverses the order of the letters in original (Hint: remember that you can concatenate strings in Python with the + operator)

original = 'semordnilap'
result = ____ 
for char in original:
    result = ____

print(result)

Practice Accumulating#

Fill in the blanks in each of the programs below to produce the indicated result.

3. Concatenate all of the words in the list into one string#

(correct answer is "redgreenblue")

words = ['red', 'green', 'blue']
result = ____

for ____ in ____:
    ____
    
print(result)

4. Create acronym: [“red”, “green”, “blue”] = “RGB”#

Write all the code this time!

Hint: Python as a .upper() method that works on characters and strings

Cumulative Sum#

Reorder and properly indent the lines of code below so that they print a list with the cumulative sum of data. The result should be [1, 3, 5, 10].

cumulative.append(sum)
for number in data:
cumulative = []
sum += number
sum = 0
print(cumulative)
data = [1, 2, 2, 5]

Identifying Item Errors#

  1. Read the code below and try to identify what the errors are without running it.

  2. Run the code, and read the error message. What type of error is it?

  3. Fix the error.

seasons = ['Spring', 'Summer', 'Fall', 'Winter']
print('My favorite season is ', seasons[4])

Summary of Key Points:#

  • A for loop executes commands once for each value in a collection.

  • A for loop is made up of a collection, a loop variable, and a body.

  • The first line of the for loop must end with a colon, and the body must be indented.

  • Indentation is always meaningful in Python.

  • Loop variables can be called anything (but it is strongly advised to have a meaningful name to the looping variable).

  • The body of a loop can contain many statements.

  • Use range to iterate over a sequence of numbers.

  • The accumulator pattern turns many values into one.

  • List comprehension is a powerful way to create lists using a for loop, in a single line of code.

  • Mapped lambda functions are another way of performing an operation(s) over a collection of values


This lesson is adapted from the Software Carpentry Plotting and Programming in Python workshop.