Python - Flow Control
Author(s) | The Carpentries Helena Rasche Donny Vrins Bazante Sanders |
Reviewers |
OverviewQuestions:Objectives:
How can my programs do different things based on data values?
Requirements:
Write conditional statements including
if
,elif
, andelse
branches.Correctly evaluate expressions containing
and
andor
.
Time estimation: 40 minutesLevel: Introductory IntroductorySupporting Materials:Published: Apr 25, 2022Last modification: Feb 13, 2023License: Tutorial Content is licensed under Creative Commons Attribution 4.0 International License. The GTN Framework is licensed under MITpurl PURL: https://gxy.io/GTN:T00088version Revision: 3
Best viewed in a Jupyter NotebookThis tutorial is best viewed in a Jupyter notebook! You can load this notebook one of the following ways
Run on the GTN with JupyterLite (in-browser computations)
Launching the notebook in Jupyter in Galaxy
- Instructions to Launch JupyterLab
- Open a Terminal in JupyterLab with File -> New -> Terminal
- Run
wget https://training.galaxyproject.org/training-material/topics/data-science/tutorials/python-flow/data-science-python-flow.ipynb
- Select the notebook that appears in the list of files on the left.
Downloading the notebook
- Right click one of these links: Jupyter Notebook (With Solutions), Jupyter Notebook (Without Solutions)
- Save Link As..
“Flow Control” is how we describe when we change the flow of code’s execution, based on some conditions. Here we’ll learn how to take different actions depending on what data out program sees, or how to run code only if some condition is true.
CommentThis tutorial is significantly based on the Carpentries Programming with Python and Plotting and Programming in Python, which are licensed CC-BY 4.0.
Adaptations have been made to make this work better in a GTN/Galaxy environment.
AgendaIn this tutorial, we will cover:
Comparators
In Python we have the following comparators to do compare two values
>
: greater than<
: less than==
: equal to!=
: does not equal>=
: greater than or equal to<=
: less than or equal to
They’re all “binary” comparators, we can only compare two values at a time.
print(37 < 38)
print(38 < 38)
print(39 < 38)
These print out True
or False
, these are the two possible values of the boolean datatype in Python.
We can use <=
to check if it’s less than or equal to:
print(19 <= 20)
print(20 <= 20)
print(21 <= 20)
And we can use ==
for comparing numbers in Python
print(11 == 11)
print(11 != 11)
print(22 != 33)
And now that we can compare numbers, we can start doing useful things with them!
Conditionals
We can ask Python to take different actions, depending on a condition, with an if
statement:
num = 37
if num > 100:
print('greater')
else:
print('not greater')
print('done')
The second line of this code uses the keyword if
to tell Python that we want to make a choice.
If the test that follows the if
statement is true,
the body of the if
(i.e., the set of lines indented underneath it) is executed, and “greater” is printed.
If the test is false,
the body of the else
is executed instead, and “not greater” is printed.
Only one or the other is ever executed before continuing on with program execution to print “done”:
Conditional statements don’t have to include an else
. If there isn’t one,
Python simply does nothing if the test is false:
num = 53
print('before conditional...')
if num > 100:
print(f'{num} is greater than 100')
print('...after conditional')
Question: If behaviourTry changing the
num
value and see what happens for different values.What happens if
num
is a:
- 202
- 3.145
- “test”
- 100.000001
- Condition is activated!
- Nothing, but not because it is a float! Because it’s less than 100
- Traceback, a
TypeError
, you cannot compare strings with integers- Condition is activated!
Multiple Branches
But what if you want more branches? What if you need to handle more cases? elif
to the rescue!
We can chain several tests together using elif
, which is short for “else if”.
if todays_temperature > 30:
print("Wear shorts! Remember your sunscreen")
elif todays_temperature > 20:
print("It's nice weather finally! Gasp!")
elif todays_temperature < 10:
print("Time to bundle up!")
else:
print("Dress normally")
if/elif/else cases follow these rules:
- must start with an
if
- can have 0 or more
elif
conditions- can have 0 or 1
else
condition (if no else condition is supplied, it’s equivalent toelse: <nothing>
)
Each of these three sections is a branch, the code pauses, and chooses to go down one of the branches based on the conditions.
The following Python code uses elif
to print the sign of a number.
num = -3
if num > 0:
print(f'{num} is positive')
elif num == 0:
print(f'{num} is zero')
else:
print(f'{num} is negative')
NB: To test for equality we use a double equals sign ==
rather than a single equals sign =
which is used to assign values.
Combining Tests
We can also combine tests using and
and or
.
and
is only true if both parts are true:
a = 1
b = -1
if (a > 0) and (b <= 0):
print('both parts are true')
else:
print('at least one part is false')
Question: Predict what happensPredict the outcomes of the following values of
a
andb
above. Predicting what you think the code will do is a useful skill to practice
- a = 0; b = -1
- a = 0; b = 10
- a = 4; b = -22
- a = 99; b = 99
- at least one part is false
- at least one part is false
- both parts are true
- at least one part is false
while or
is true if at least one part is true:
a = 1
b = -1
if (a < 0) or (b > 0):
print('at least one test is true')
True
andFalse
are special words in Python calledbooleans
, which represent truth values. A statement such as1 < 0
returns the valueFalse
, while-1 < 0
returns the valueTrue
.
True
and False
booleans are not the only values in Python that are true and false.
In fact, any value can be used in an if
or elif
. This is commonly used to
check, for instance, if a string is empty or if some data is provided:
if '':
print('empty string is true')
if 'word':
print('word is true')
You can also use it to check if a list is empty or full:
if []:
print('empty list is true')
if [1, 2, 3]:
print('non-empty list is true')
# The last statement is equivalent to:
if len([1, 2, 3]) > 0:
print('non-empty list is true')
Or you can check if a number is zero, or non-zero:
if 0:
print('zero is true')
if 1:
print('one is true')
Inverting Conditions
Sometimes it is useful to check whether some condition is not true.
The Boolean operator not
can do this explicitly.
After reading and running the code below,
write some if
statements that use not
to test the rule
that you formulated in the previous question.
not
is a unary
(not binary
) operator: it only takes a single value
if not '':
print('empty string is not true')
if not 'word':
print('word is not true')
if not not True:
print('not not True is true')
Ranges
Python makes it super easy to check if a number is within a range.
quality_score = 32 # Try out different values!
if quality_score > 40:
print("Your data is a bit sus")
elif 20 < quality_score <= 40:
print("Hey that looks ok")
elif 4 < quality_score <= 20:
print("Oh you did nanopore sequencing")
else:
print("It shouldn't be *that* bad. Try again.")
There are two important points here:
20 < x < 40
is equivalent to20 < x and x < 40
, checking both sides of the condition, to make sure it’s greater than one value and smaller than another- Note that we checked in the second case
20 < x
and then in the third we had to checkx <= 20
. If we had not had a<=
on one side, what would have happened to 20? It would have gone straight to else!
Exercises
Question
if
s,elif
s andelse
s get evaluated in blocks. Look at the following code and list the lines that are part of a single block.1. if x: 2. # .. 3. if y: 4. # .. 5. elif z: 6. # .. 7. if q: 8. # .. 9. else: 10. # .. 11. elif t: 12. # .. 13. else e: 14. # ..
“Blocks” of if/elif/elses
- must start with an
if
- can have 0 or more
elif
conditions- can have 0 or 1
else
condition (if no else condition is supplied, it’s equivalent toelse: <nothing>
)The above blocks are parsed together, you could not insert a
- 1-2, Just an
if
by itself. There’s no elif, or else, so that’s the end of that block- 3-6,
if
andelif
get evaluated, there is noelse
, so that’s the end of that block- 7-10,
if
andelse
is fine- 11-14, error! This is missing an
if
case, it will fail with a syntaxerror.
# Test code here.
Question: How Many Paths?Consider this code:
if 4 > 5: print('A') elif 4 == 5: print('B') elif 4 < 5: print('C')
Which of the following would be printed if you were to run this code? Why did you pick this answer?
- A
- B
- C
- B and C
C gets printed because the first two conditions,
4 > 5
and4 == 5
, are not true, but4 < 5
is true.
# Test code here.
Question: Close EnoughWrite some conditions that print
True
if the variablea
is within10
of the variableb
andFalse
otherwise. Compare your implementation with your partner’s: do you get the same answer for all possible pairs of numbers?There is a [built-in function
abs
][abs-function] that returns the absolute value of a number:print(abs(-12))
12
a = 5 b = 5.1 if abs(a - b) <= 10: print('True') else: print('False')
print(abs(a - b) <= 10)
This works because the Booleans
True
andFalse
have string representations which can be printed.
# Test code here.
Question: PitfallsA integer number between 0 and 100 will be provided to this function. Answer these two questions:
- Will it always print something? If not, which value(s) fail?
- Can you find any numbers the programmer explicitly wanted to handle, that aren’t handled as expected?
num = 42 # Randomly chosen so the code will execute, try changing it around. if num > 90: print("great score") elif num < 32: print("Very cold") elif num >= 86: print("Almost") elif num == 86: print("It's exactly this value!") elif 32 < num < 58: print("Getting warmer") elif 57 < num <= 86: print("Everything else goes here")
- No, it won’t. 32 is the only value there that doesn’t print anything. You can either do
x < 57
and later57 <= x
to test the bigger and smaller values, or you can make usex < 57
and56 < x
, which have the same results, but only with integers. If your code accepted a float, e.g.56.5
, both of those tests would be true. Sox < 57
and later57 <= x
is the preferred way to write that.86
is the most obvious solution to this, the programmer added a check specifically to see if the value was 86, but instead it’s caught by the previous case.
num = 42 # Randomly chosen so the code will execute, try changing it around.
if num > 90:
print("great score")
elif num < 32:
print("Very cold")
elif num >= 86:
print("Almost")
elif num == 86:
print("It's exactly this value!")
elif 32 < num < 58:
print("Getting warmer")
elif 57 < num <= 86:
print("Everything else goes here")
Complicated if/elif/else cases are common in code, you need to be able to spot these sort of issues. For example there are large if/else cases in the Galaxy codebase, sometimes nested even, and being ale to predict their behaviour is really important to being able to work with the code. Missing else cases are sometimes important, sometimes a bug, sometimes just the code hasn’t been implemented yet, which is why we always write good code comments!