Flow Control in Python

Video

What is Flow Control?

What we have already discussed is very useful, and machines for performing computations are old and varied.

However, in the eyes of most philosophers of programming, the one thing that separates programming from previous automation - such as the Jaquard loom - is its ability to make decisions as to what to do next. In the programming world, that is known as flow control - control of the "flow" of executed code.

Types of Flow Control

There are countless different systems for flow control in programming, but four are by far the most common:

Conditionals

The most basic type of flow control is the humble if statement:

x=3
if x>10:
    print("x is big.")
if x<= 10:
    print("x is small.")

These allow the program to make more complicated decisions.

If we have opposite conditions, we can use the else statement:

x=3
if x>10:
    print("x is big.")
else:
    print("x is small.")

And chain multiple conditions together with elif:

x=3
if x>10:
    print("x is big.")
elif x>0:
    print("x is small.")
else:
    print("x is not positive.")

Loops

The simplest loop is a while loop. A while loop is given a condition, and executes the loop until the condition is false. (If the condition is false, it does nothing.)

# Setup Variables
moduland = 50
modulo = 7
# Compute Modulus
modulus = moduland
while modulus > modulo:
    modulus = modulus - modulo

The most common loop, however, is for. Most languages have for loops that are simply while loops with some syntactic help. Python for loops are more versatile - they loop through containers.

example_list = [1,4,9]
cube_list = []

for value in example_list:
    cube_list.append(value**3)

print(cube_list)

Often, this is enough. However, you often want to iterate a certain number of times.

For that, there is the range command:

for i in range(5):
      print(i)

Note that range(5) provides the first 5 integers, 0-indexed; that is, 0,1,2,3,4.

Range accepts optional parameters for starting value and integer step; for example,

for i in range(1,10,2):
  print(i)

prints 1,3,5,7,9.

Breaks & Continues

We will talk about escaping more in the future, but there are two useful flow control statements within loops:

break escapes the current loop, and moves on after it. For example:

for i in range(100):
  if i==5:
    break
print(i)

This is mostly used for searching for particular values - iterating until something happens.

It is a better pattern than while loops for high intensity computing because of your easy ability to set an upper limit for loop length. But if the calculation fails, you need a way to determine it: fortunately, for supports an else clause which triggers if and only if the loop exists without hitting a break statement.

for i in range(100):
  if i==5:
    break
else:
  print("Unable to find 5.")
print(i)

break only breaks out of the closest loop. There is no way to easily break out of a higher loop just using break.

continue works similarly, but instead of escaping the loop, it starts the next iteration immediately:

for i in range(10):
  if i%3 != i%2:
    continue
  print(i)

This is great for discarding cases, and is a common task in the algorithm

Functions

Functions very much count as flow control.

The main two behaviors of functions that control flow are entering and exiting.

When you call a function, execution of the parent flow is paused. Until the function executes a return statement - or runs out of things to do - you are within the function's flow.

This can be used to escape any number of loops: for example,

def find_5():
  for i in range(100):
    for j in range(20):
      if i == 5:
        return i
  print("Couldn't find 5.")

would not print its statement; it would find its value and return.

Errors

We will talk about errors in more detail later in the course.

For flow control, it is most important to know that they are raised by functions whenever they can't do what you asked. They break out of all flow - unless they are suppressed.

If your code is asked to do something it doesn't know how to do, it is best to raise an error:

raise ValueError("An Explanation of What Went Wrong.")

There are many types of builtin errors. Libraries - and you - are allowed to define new errors, which can help with specificity.

For now, the ones we will use are:

error example situation
TypeError raise TypeError("Explanation") used when a function is passed an argument of a type it cannot use.
ValueError raise ValueError("Explanation") used when a function is passed an argument of a type it cannot use.

Python Comprehensions

Python provides an abbreviated syntax for creating containers using inline flow control.

For example, to construct a list of the first 10 positive cubes, you can do:

first_10_squares = [i**3 for i in range(1,11)]

Or an example from an exploratory file in my research,

quadratic_residues = {pow(i,2,p) for i in range(p//2)}

While it can be used for complex systems, I recommend shying away from it for anything that isn't immediately clear.

Using iterable containers, list comprehensions are often used as a simple interface for filters and maps: we can pick out the floats with integer values with:

[int(i) for i in example_numbers if i.is_integer()]

You can still use the concept of maps and filters - which we are familiar with from mathematics - using the map and filter functions: the following two statements are equivalent.

[float(i) for i in range(10) if i%2 == i%3]
list(map(float, filter(lambda x: x%2 == x%3, range(10))))

Which may give you some insight into why people prefer the comprehension syntax.

Generators

Generators are mostly outside the scope of this class. However, there is one reason we must bring them up; you cannot create tuples with a comprehension directly; they instead create a generator.

You can think of generators as single-use lists. This makes them considerably faster, and with a lower memory footprint, in many use cases. We have already talked about a few generators - in Python 3, range is a generator.

These can be used as arguments without the enclosing. For example, you could construct a string containing the integers with:

' '.join(str(i**2) for i in range(20))

To convert a generator to a reusable list, it is a simple typecast:

example_list = list(range(10))

Or to construct a tuple,

example_tuple = tuple(i**2 for i in example_list)

worksheet

Today's worksheet will get you using loops and comprehensions, as well as some design.

Department of Mathematics, Purdue University
150 N. University Street, West Lafayette, IN 47907-2067
Phone: (765) 494-1901 - FAX: (765) 494-0548
Contact the Webmaster for technical and content concerns about this webpage.
Copyright© 2018, Purdue University, all rights reserved.
West Lafayette, IN 47907 USA, 765-494-4600
An equal access/equal opportunity university
Accessibility issues? Contact the Web Editor (webeditor@math.purdue.edu).