{ "metadata": { }, "nbformat": 4, "nbformat_minor": 5, "cells": [ { "id": "metadata", "cell_type": "markdown", "source": "
if
, elif
, and else
branches.\n- Correctly evaluate expressions containing and
and or
.\n\n**Time Estimation: 40M**\n“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.
\n\n\n💬 Comment
\nThis tutorial is significantly based on the Carpentries Programming with Python and Plotting and Programming in Python, which are licensed CC-BY 4.0.
\nAdaptations have been made to make this work better in a GTN/Galaxy environment.
\n
\n\nAgenda
\nIn this tutorial, we will cover:
\n\n
\n- Comparators
\n
In Python we have the following comparators to do compare two values
\n>
: greater than<
: less than==
: equal to!=
: does not equal>=
: greater than or equal to<=
: less than or equal toThey’re all “binary” comparators, we can only compare two values at a time.
\n", "cell_type": "markdown", "metadata": { "editable": false, "collapsed": false } }, { "id": "cell-1", "source": [ "print(37 < 38)\n", "print(38 < 38)\n", "print(39 < 38)" ], "cell_type": "code", "execution_count": null, "outputs": [ ], "metadata": { "attributes": { "classes": [ "python" ], "id": "" } } }, { "id": "cell-2", "source": "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:
And we can use ==
for comparing numbers in Python
print(11 == 11)\nprint(11 != 11)\nprint(22 != 33)\n
And now that we can compare numbers, we can start doing useful things with them!
\nWe can ask Python to take different actions, depending on a condition, with an if
statement:
The second line of this code uses the keyword if
to tell Python that we want to make a choice.\nIf the test that follows the if
statement is true,\nthe body of the if
\n(i.e., the set of lines indented underneath it) is executed, and “greater” is printed.\nIf the test is false,\nthe body of the else
is executed instead, and “not greater” is printed.\nOnly 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,\nPython simply does nothing if the test is false:
\n\n❓ Question: If behaviour
\nTry changing the
\nnum
value and see what happens for different values.What happens if
\nnum
is a:\n
\n- 202
\n- 3.145
\n- “test”
\n- 100.000001
\n\n\nHint: Select the text with your mouse to see the answer👁 Solution
\n\n
\n- Condition is activated!
\n- Nothing, but not because it is a float! Because it’s less than 100
\n- Traceback, a
\nTypeError
, you cannot compare strings with integers- Condition is activated!
\n
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:\n print(\"Wear shorts! Remember your sunscreen\")\nelif todays_temperature > 20:\n print(\"It's nice weather finally! Gasp!\")\nelif todays_temperature < 10:\n print(\"Time to bundle up!\")\nelse:\n print(\"Dress normally\")\n
\n\n💡 Tip: If/Elif/Elif/Elif/Else:
\nif/elif/else cases follow these rules:
\n\n
\n- must start with an
\nif
- can have 0 or more
\nelif
conditions- can have 0 or 1
\nelse
condition (if no else condition is supplied, it’s equivalent toelse:
</code>)
Each of these three sections is a branch, the code pauses, and chooses to go down one of the branches based on the conditions.
\nThe following Python code uses elif
to print the sign of a number.
NB: To test for equality we use a double equals sign ==
\nrather than a single equals sign =
which is used to assign values.
We can also combine tests using and
and or
.\nand
is only true if both parts are true:
\n\n❓ Question: Predict what happens
\nPredict the outcomes of the following values of
\na
andb
above. Predicting what you think the code will do is a useful skill to practice\n
\n- a = 0; b = -1
\n- a = 0; b = 10
\n- a = 4; b = -22
\n- a = 99; b = 99
\n\n\nHint: Select the text with your mouse to see the answer👁 Solution
\n\n
\n- at least one part is false
\n- at least one part is false
\n- both parts are true
\n- at least one part is false
\n
while or
is true if at least one part is true:
\n\n💡 Tip:
\nTrue
andFalse
\n
True
andFalse
are special words in Python calledbooleans
,\nwhich represent truth values. A statement such as1 < 0
returns\nthe valueFalse
, while-1 < 0
returns the valueTrue
.
True
and False
booleans are not the only values in Python that are true and false.\nIn fact, any value can be used in an if
or elif
. This is commonly used to\ncheck, for instance, if a string is empty or if some data is provided:
You can also use it to check if a list is empty or full:
\n", "cell_type": "markdown", "metadata": { "editable": false, "collapsed": false } }, { "id": "cell-17", "source": [ "if []:\n", " print('empty list is true')\n", "if [1, 2, 3]:\n", " print('non-empty list is true')\n", "# The last statement is equivalent to:\n", "if len([1, 2, 3]) > 0:\n", " print('non-empty list is true')" ], "cell_type": "code", "execution_count": null, "outputs": [ ], "metadata": { "attributes": { "classes": [ "python" ], "id": "" } } }, { "id": "cell-18", "source": "Or you can check if a number is zero, or non-zero:
\n", "cell_type": "markdown", "metadata": { "editable": false, "collapsed": false } }, { "id": "cell-19", "source": [ "if 0:\n", " print('zero is true')\n", "if 1:\n", " print('one is true')" ], "cell_type": "code", "execution_count": null, "outputs": [ ], "metadata": { "attributes": { "classes": [ "python" ], "id": "" } } }, { "id": "cell-20", "source": "Sometimes it is useful to check whether some condition is not true.\nThe Boolean operator not
can do this explicitly.\nAfter reading and running the code below,\nwrite some if
statements that use not
to test the rule\nthat you formulated in the previous question.\nnot
is a unary
(not binary
) operator: it only takes a single value
Python makes it super easy to check if a number is within a range.
\n", "cell_type": "markdown", "metadata": { "editable": false, "collapsed": false } }, { "id": "cell-23", "source": [ "quality_score = 32 # Try out different values!\n", "\n", "if quality_score > 40:\n", " print(\"Your data is a bit sus\")\n", "elif 20 < quality_score <= 40:\n", " print(\"Hey that looks ok\")\n", "elif 4 < quality_score <= 20:\n", " print(\"Oh you did nanopore sequencing\")\n", "else:\n", " print(\"It shouldn't be *that* bad. Try again.\")" ], "cell_type": "code", "execution_count": null, "outputs": [ ], "metadata": { "attributes": { "classes": [ "python" ], "id": "" } } }, { "id": "cell-24", "source": "There are two important points here:
\n20 < x < 40
is equivalent to 20 < x and x < 40
, checking both sides of the condition, to make sure it’s greater than one value and smaller than another20 < x
and then in the third we had to check x <= 20
. If we had not had a <=
on one side, what would have happened to 20? It would have gone straight to else!\n\n", "cell_type": "markdown", "metadata": { "editable": false, "collapsed": false } }, { "id": "cell-25", "source": [ "# Test code here." ], "cell_type": "code", "execution_count": null, "outputs": [ ], "metadata": { "attributes": { "classes": [ "python" ], "id": "" } } }, { "id": "cell-26", "source": "❓ Question:
\n\n
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.\n1. if x:\n2. # ..\n3. if y:\n4. # ..\n5. elif z:\n6. # ..\n7. if q:\n8. # ..\n9. else:\n10. # ..\n11. elif t:\n12. # ..\n13. else e:\n14. # ..\n
\n\nHint: Select the text with your mouse to see the answer👁 Solution
\n“Blocks” of if/elif/elses
\n\n
\n- must start with an
\nif
- can have 0 or more
\nelif
conditions- can have 0 or 1
\nelse
condition (if no else condition is supplied, it’s equivalent toelse:
</code>) The above blocks are parsed together, you could not insert a
\n\n
\n- 1-2, Just an
\nif
by itself. There’s no elif, or else, so that’s the end of that block- 3-6,
\nif
andelif
get evaluated, there is noelse
, so that’s the end of that block- 7-10,
\nif
andelse
is fine- 11-14, error! This is missing an
\nif
case, it will fail with a syntaxerror.
\n\n", "cell_type": "markdown", "metadata": { "editable": false, "collapsed": false } }, { "id": "cell-27", "source": [ "# Test code here." ], "cell_type": "code", "execution_count": null, "outputs": [ ], "metadata": { "attributes": { "classes": [ "python" ], "id": "" } } }, { "id": "cell-28", "source": "❓ Question: How Many Paths?
\nConsider this code:
\n\nif 4 > 5:\n print('A')\nelif 4 == 5:\n print('B')\nelif 4 < 5:\n print('C')\n
Which of the following would be printed if you were to run this code?\nWhy did you pick this answer?
\n\n
\n- A
\n- B
\n- C
\n- B and C
\n\n\nHint: Select the text with your mouse to see the answer👁 Solution
\nC gets printed because the first two conditions,
\n4 > 5
and4 == 5
, are not true,\nbut4 < 5
is true.
\n\n", "cell_type": "markdown", "metadata": { "editable": false, "collapsed": false } }, { "id": "cell-29", "source": [ "# Test code here." ], "cell_type": "code", "execution_count": null, "outputs": [ ], "metadata": { "attributes": { "classes": [ "python" ], "id": "" } } }, { "id": "cell-30", "source": "❓ Question: Close Enough
\nWrite some conditions that print
\nTrue
if the variablea
is within10
of the variableb
\nandFalse
otherwise.\nCompare your implementation with your partner’s:\ndo you get the same answer for all possible pairs of numbers?\n\n💡 Tip: abs
\nThere is a [built-in function
\nabs
][abs-function] that returns the absolute value of\na number:\nprint(abs(-12))\n
\n12\n
\n\n👁 Solution 1
\n\na = 5\nb = 5.1\n\nif abs(a - b) <= 10:\n print('True')\nelse:\n print('False')\n
\n\n👁 Solution 2
\n\nprint(abs(a - b) <= 10)\n
This works because the Booleans
\nTrue
andFalse
\nhave string representations which can be printed.
\n\n", "cell_type": "markdown", "metadata": { "editable": false, "collapsed": false } }, { "id": "cell-31", "source": [ "num = 42 # Randomly chosen so the code will execute, try changing it around.\n", "if num > 90:\n", " print(\"great score\")\n", "elif num < 32:\n", " print(\"Very cold\")\n", "elif num >= 86:\n", " print(\"Almost\")\n", "elif num == 86:\n", " print(\"It's exactly this value!\")\n", "elif 32 < num < 58:\n", " print(\"Getting warmer\")\n", "elif 57 < num <= 86:\n", " print(\"Everything else goes here\")" ], "cell_type": "code", "execution_count": null, "outputs": [ ], "metadata": { "attributes": { "classes": [ "python" ], "id": "" } } }, { "id": "cell-32", "source": "❓ Question: Pitfalls
\nA integer number between 0 and 100 will be provided to this function. Answer these two questions:
\n\n
\n- Will it always print something? If not, which value(s) fail?
\n- Can you find any numbers the programmer explicitly wanted to handle, that aren’t handled as expected?
\n\nnum = 42 # Randomly chosen so the code will execute, try changing it around.\nif num > 90:\n print(\"great score\")\nelif num < 32:\n print(\"Very cold\")\nelif num >= 86:\n print(\"Almost\")\nelif num == 86:\n print(\"It's exactly this value!\")\nelif 32 < num < 58:\n print(\"Getting warmer\")\nelif 57 < num <= 86:\n print(\"Everything else goes here\")\n
\n\nHint: Select the text with your mouse to see the answer👁 Solution
\n\n
\n- No, it won’t. 32 is the only value there that doesn’t print anything. You can either do
\nx < 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.- \n
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.
\n\n", "cell_type": "markdown", "metadata": { "editable": false, "collapsed": false } }, { "cell_type": "markdown", "id": "final-ending-cell", "metadata": { "editable": false, "collapsed": false }, "source": [ "# Key Points\n\n", "- Use `if condition` to start a conditional statement, `elif condition` to provide additional tests, and `else` to provide a default.\n", "- The bodies of the branches of conditional statements must be indented.\n", "- Use `==` to test for equality.\n", "- `X and Y` is only true if both `X` and `Y` are true.\n", "- `X or Y` is true if either `X` or `Y`, or both, are true.\n", "- Zero, the empty string, and the empty list are considered false; all other numbers, strings, and lists are considered true.\n", "- `True` and `False` represent truth values.\n", "- `not` can be used to invert the condition\n", "\n# Congratulations on successfully completing this tutorial!\n\n", "Please [fill out the feedback on the GTN website](https://training.galaxyproject.org/training-material/topics/data-science/tutorials/python-flow/tutorial.html#feedback) and check there for further resources!\n" ] } ] }💡 Tip: Why a synthetic example like this?
\nComplicated 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!
\n