Python for Science

Control flow and functions

Python Official Documentation

On this page we will take a look at control flow and functions.

  1. Control flow - Ways to alter the top to bottom execution of code.
  2. Function - Named group of lines of code that can be executed by invoking its name.

It's not as scary as it sounds.

Control flow

The Python Tutorial - 4. More Control Flow Tools

I linked the section on control flow from Python's official tutorial above. I find the tutorial a bit hard to follow in general, but this section seems alright. I won't go through everything from the tutorial.

We will take a look at:

  • if, else and elif
  • while loops
  • for loops
  • break
  • continue
  • and functions a bit later on

Everything on this page is essential to programming in many different programming languages. With these building blocks you are able to start making things.

TODO: I should write about try / except later.

if, else and elif

There are cases in life where depending on some factor you either want to do one thing or do some other thing. For example, if you are studying for a test you may want to focus on studying the parts you don't know well. If you are worse at statistics than heat flux then you should study statistics, otherwise (else) you should study heat flux. Software also need to do these kinds of decisions. if, else and elif allows your code to make decisions. In one case do this, in another case do that.

Let's look at a case where you either want to do something or do nothing.

ENERGY_THRESHOLD = 5
sleep_hours = 8
hours_since_last_meal = 4

if sleep_hours - hours_since_last_meal > ENERGY_THRESHOLD:
    print("I am going to study!")

This program tells me if I should study (or not). I run this program every day. Notice how I put ENERGY_THRESHOLD in capital letters. This tells other developers that the variable is a constant and should not be changed after it has been assigned a value.

Let's look at the syntax for if. There's an if-statement (the characters "if"), followed by an expression that evaluates to True or False, followed by a colon. The line after the colon is indented, indicating we're in a new block of code. The indented code is only run if the expression in the if-statement evaluates to True.

Let's put the "study statistics or heat flux" example into code.

STATISTICS_PROFICIENCY = 4
HEAT_FLUX_PROFICIENCY = 2

if STATISTICS_PROFICIENCY < HEAT_FLUX_PROFICIENCY:
    print("I am going to study statistics!")
else:
    print("I am going to study heat flux!")

We have an if-statement with a condition. As before, if the if-statement is True, the indented code on the row below is run. However, if the if-statement is False, the indented code under else: is run instead.

We have seen cases with one if and one else. What if we want to handle more cases?

current_energy_level = 5

if current_energy_level >= 7:
    print("I should go to the big store far away for groceries.")
elif current_energy_level >= 5:
    print("I should go to the medium size store that's fairly close.")
elif current_energy_level >= 3:
    print("I should go to the small store that's around the corner.")
else:
    print("I should rest.")

"elif" is short for "else if". else if is common in other programming languages, but it's called elif in Python. Here we use elif followed by expressions that evaluates to True or False to decide which store to visit. You can have zero or more elifs. You can also have elif without else. You cannot have elif or else without if.

Now here's an interesting question: What will the output be? current_energy_level is 5, so looking at the code it does not seem unreasonable that the code for both elifs and the else will run. In actuality the output will be "... medium size store ...". The code runs from top to bottom. When a condition is True, the indented code under the if statement runs and the other conditions are not even evaluated. The fact that the other conditions are not evaluated can have implications for performance, but that's a different topic.

Let's look at one final thing about if-statements. Namely nesting.

current_energy_level = 5

if current_energy_level < 3:
    print("I should rest.")
else:
    if current_energy_level >= 7:
        print("I should go to the big store far away for groceries.")
    elif current_energy_level >= 5:
        print("I should go to the medium size store that's fairly close.")
    else:
        print("I should go to the small store that's around the corner.")

The code above is almost equivalent to the previous example. It produces the same result but does it in a different way. Under the else case there's another if/elif/else-statement. You can nest if-statements how much you want. In this case ("case" as in "situation") I think the nested statement doesn't help with readability, but in other cases it might.

Loops

Often you want to run some specific lines of code many times, maybe once for every item in a list. You can do this with loops. Python has two different kinds of loops.

  • while - Runs while the provided condition is True.
  • for - Iterates through all elements in a sequence (for example list or range).

Both of these loops supports the keywords break and continue, which we will look at soon.

Let's look at a while loop.

x = 5
while x > 0:
    print(x)
    x -= 1      # Fancy shorthand for x = x - 1

The while loop takes a condition, x > 0 in this case, and runs the loop body (indented code) until the condition is False. After each iteration it will re-evaluate the condition. This code will output:

5
4
3
2
1

Let's unroll the loop (write equivalent code but without the loop)!

x = 5
print(x)
x -= 1
print(x)
x -= 1
print(x)
x -= 1
print(x)
x -= 1
print(x)
x -= 1

When you only loop five times and the code inside the loop is simple you don't really need a loop, but I think even in this case the while loop version looks nicer. However, if you want to repeat the same action 5000 times, or an unknown number of times, you definitely want a loop.

What do you think this does?

while True:
    print("AAAAAAAAAAAAAHHHHH!")

Correct! It will loop and print "AAAAAAAAAAAAAHHHHH!" forever!

for loops are a bit more interesting. In Python, for loops expects a sequence they can iterate over. We will look at iterating over a list and over a range. Let's start with a list!

cool_greetings = ["What's up?", "Howdy!", "*nods head*"]
for greeting in cool_greetings:
    print(greeting)

Predictably the output will be:

What's up?
Howdy!
*nods head*

The syntax is for, followed by a name that will be assigned to the current element in the loop body, then in, followed by the target sequence.

The range() type is often used with for loops.

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

The output of this loop is:

0
1
2
3
4

A range generates a list of numbers that you can loop through. One interesting detail is that range(4) does not generate the complete [0, 1, 2, 3, 4] list at once, instead it only generates the next value as needed. In fact, the function only keeps track of three values at any given time.

Sometimes you want to access list elements via an index (position).

cool_greetings = ["What's up?", "Howdy!", "*nods head*"]
for i in range(len(cool_greetings)):
    print(cool_greetings[i])

The length of cool_greetings is 5, so range(len(cool_greetings)) becomes range(3). The first item in a list is located at index 0, the variable i is assigned a value produced by range(3) each iteration. In the first iteration i is 0, so print(cool_greetings[i]) will print the greeting located at index 0. And so on.

Break - exit a loop early

Exiting a loop early can be useful. If you're after something specific in a list and want to leave when you've achieved whatever task you wanted to you can use the break keyword to exit the loop. This is also good for performance. No need to loop more than necessary.

hide_and_seek = ["Old man", "Waldo", "Lambert", "John"]
for hider in hide_and_seek:
    if hider == "Waldo":
        print("Waldo is hiding in this list!")
        break

In this case we've been given a list of strings and our employer wants to know if the string "Waldo" is hiding in somewhere in the list. We loop through the elements, check if the current element, hider, is equal to "Waldo" and exit the loop if it is.

Continue - skip current loop iteration

In some cases you want to skip the current loop iteration and jump to the next.

temperatures_c = [31.2, 30.1, None, 12.3]
for t in temperatures_c:
    if t == None:
        print("Found invalid data. Skipping current iteration.")
        continue
    print(f"{t} C = {t * 9/5 + 32} F") # Fancy f-string format

In the code above we loop through a list of temperatures that's presumably in celsius. If we run across invalid data, represented by None in this case, we skip the current iteration. Otherwise we print the value converted to fahrenheit.

This is the output:

31.2 C = 88.16 F
30.1 C = 86.18 F
Found invalid data. Skipping current iteration.
12.3 C = 54.14 F

Both while and for loops support break and continue.

Functions

The Python Tutorial - 4. More Control Flow Tools - Defining Functions

A function is a named group of lines of code that performs some action and optionally returns a value. Functions are a helpful way to organize code and expose functionality.

From the tutorial: "The keyword def introduces a function definition. It must be followed by the function name and the parenthesized list of formal parameters. The statements that form the body of the function start at the next line, and must be indented." To call a function you write the name, followed by parenthesis and the required parameters (if any).

It's easier to understand in code:

def celsius_to_fahrenheit(temp_in_c):
    return temp_in_c * 9/5 + 32

result = celsius_to_fahrenheit(20)
print(result) # 68.0

The function celsius_to_fahrenheit() takes a parameter temp_in_c, calculates the degrees in fahrenheit and returns the result using the return statement. When the code reaches a return statement, the function finishes. If you have put code after return, that code will not run.

As mentioned in the tutorial, you can start the first line of the function body (the indented code), with a string literal describing what the function does. The string in that context is called a "docstring", and it should, by convention, be written in with triple quotes. The PEP (Python Enhancement Proposal) for docstrings is found here: PEP 257 - Docstring Conventions. PEPs are sort of agreed upon specifications, or conventions, for Python.

def celsius_to_fahrenheit(temp_in_c):
    """Takes temperature in celsius as a number and returns it in fahrenheit."""
    return temp_in_c * 9/5 + 32

Above is an example of what a docstring can look like. Below I will link the documentation for the request class in a Python web framework called Falcon. I will also link the source code for the class. I'm only adding these links to illustrate that docstrings can be very detailed (which is good when you write documentation for external users).

Documentation: falcon.Request

Source code (note the detailed docstring): falcon.Request

There are more things regarding functions that can be useful to know:

  • Functions without return implicitly returns the value None.
  • Functions can return other functions.
  • You can have default values for parameters.
  • You can write functions that doesn't take parameters, but you still need ().
  • The actual values you pass to a function when you invoke it are called "arguments". In the first celcius_to_fahrenheit() example, 20 is the argument and temp_in_c is the parameter.
  • Skipping ahead: There's a concept similar to functions called methods. They are like functions except tied to a class.

You can read more about functions in the official Python tutorial I linked earlier.

Next: Exercises