Overview

Teaching: 15 min
Exercises: 5 min
Questions
  • How can programs do different things for different data?

Objectives
  • Correctly write programs that use if and else statements and simple Boolean expressions (without logical operators).

  • Trace the execution of unnested conditionals and conditionals inside loops.

Comparisons in Python

Python allows us to test the truth of statements using certain functions as well as by using what are called comparison operators. These include >, ‘<’, >=, <=, ==, !=, is, is not

Some examples of using these operators:

5 > 4
True
5 == 5
True
5 != 5
False
 5 is not 5.0
True

The assert statement

The assert statement is an important tool available to us to make sure our programs behave as we intend. We follow the assert command by an expression that it will assess for truth. If the expression False is returned our program will not execute. This is far better than continuing further to produce an obscure runtime error. Worse still if our program is executed with some of our assumptions not mess it may generate some content in error and we would have no idea this has happened.

Use if statements to control whether or not a block of code is executed.

metasearch_dir = Path('data_not_in_repo/metasearch')
data_exists = metasearch_dir.exists()
if data_exists:
    print('Data directory has been downloaded')
Data directory has been downloaded

If the statement does not evaluate to True the indented code will not be evaluated and in this case nothing will be printed:

fake_dir = Path('metasearch/fast_cars')
if fake_dir.exists():
    print('Directory exists!')

Recording changes to our analysis.

We should edit our script at this point to tidy it up. What changes can we make that will best promote reproducibility? Ideally we would have written a conditional statement at the beginning of our analysis so that we are certain that both conditions of our if/else code block evaluated. In reality we will always have bits of code that we have to add to our code in retrospect. Ideally at this point we would delete our data directory to check that every condition runs.

Use else to execute a block of code when an if condition is not true.

fake_dir = Path('metasearch/fast_cars')
if fake_dir.exists():
    print('Directory exists!')
else:
    print('There is no directory with the path: ', fake_dir)
There is no directory with the path: metasearch/fast_cars

Use elif to specify additional tests.

from pathlib import Path
metasearch_dir = Path('data_not_in_repo/metasearch')
data_exists = metasearch_dir.exists()
in_repro_course = Path.cwd().name == 'repro_course'
if data_exists and in_repro_course:
    print('Data directory has been downloaded')
elif in_repro_course:
    !git clone https://github.com/OpenNeuroLab/metasearch.git {metasearch_dir}
else:
    assert(False)
Data directory has been downloaded

We can see that at this point if we change to another directory our script will no longer display text as expected when we execute it:

%cd .. # to change into the parent directory
%run repro_course/metasearch_analysis.py

If we run our file in debug mode and then press c for continue we can see the exception we raised with our assert statement.

%run -d repro_course/metasearch_analysis.py
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
/usr/local/Anaconda/envs/py3.5/lib/python3.5/site-packages/IPython/core/interactiveshell.py in safe_execfile(self, fname, *where, **kw)
   2479                 py3compat.execfile(
   2480                     fname, glob, loc,
-> 2481                     self.compile if kw['shell_futures'] else None)
   2482             except SystemExit as status:
   2483                 # If the call was made with 0 or None exit status (sys.exit(0)

/usr/local/Anaconda/envs/py3.5/lib/python3.5/site-packages/IPython/utils/py3compat.py in execfile(fname, glob, loc, compiler)
    184         with open(fname, 'rb') as f:
    185             compiler = compiler or compile
--> 186             exec(compiler(f.read(), fname, 'exec'), glob, loc)
    187 
    188     # Refactor print statements in doctests.

/home/rodgersleejg/repro_course/metasearch_analysis.py in <module>()
      1 # coding: utf-8
----> 2 from pathlib import Path
      3 metasearch_dir = Path('data_not_in_repo/metasearch')
      4 data_exists = metasearch_dir.exists()
      5 in_repro_course = Path.cwd().name == 'repro_course'

AssertionError: 

We will now change back to our repro_course directory to continue with our analysis.

%cd repro_course
/home/this_user/repro_course

Final points on conditionals

[ x for x in range(10) if x > 4]
[5, 6, 7, 8, 9]
import itertools
meta_exist = ['metasearch in pwd', 'metasearch not in pwd']
repro_as_pwd = ['repro_course is pwd', 'repro_course not pwd']
list(itertools.product(meta_exist, repro_as_pwd))
[('metasearch in pwd', 'repro_course is pwd'),
 ('metasearch in pwd', 'repro_course not pwd'),
 ('metasearch not in pwd', 'repro_course is pwd'),
 ('metasearch not in pwd', 'repro_course not pwd')]

Saving our work.

First we should search for our command where we used an else statement (%hist -n -g else) then we should append this command to our script.

%save -a metasearch_analysis.py 42 # enter the number of the multi-line if-else statement
The following commands were written to file `metasearch_analysis.py`:
from pathlib import Path
metasearch_dir = Path('data_not_in_repo/metasearch')
data_exists = metasearch_dir.exists()
in_repro_course = Path.cwd().name == 'repro_course'
if data_exists and in_repro_course:
    print('Data directory has been downloaded')
elif in_repro_course:
    get_ipython().system('git clone https://github.com/OpenNeuroLab/metasearch.git {metasearch_dir}')
else:
    assert(False)

Identifying Variable Name 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 NameError do you think this is? Is it a string with no quotes, a misspelled variable, or a variable that should have been defined but was not?
  3. Fix the error.
  4. Repeat steps 2 and 3, until you have fixed all the errors.
for number in range(10):
    # use a if the number is a multiple of 3, otherwise use b
    if (Number % 3) == 0:
        message = message + a
    else:
        message = message + "b"
print(message)

Compound Relations Using and, or, and Parentheses

Often, you want some combination of things to be true. You can combine relations within a conditional using and and or. Continuing the example above, suppose you have

mass     = [ 3.54,  2.07,  9.22,  1.86,  1.71]
velocity = [10.00, 20.00, 30.00, 25.00, 20.00]

i = 0
for i in range(5):
    if mass[i] > 5 and velocity[i] > 20:
        print "Fast heavy object.  Duck!"
    elif mass[i] > 2 and mass[i] <= 5 and velocity[i] <= 20:
        print "Normal traffic"
    elif mass[i] <= 2 and velocity <= 20:
        print "Slow light object.  Ignore it"
    else:
        print "Whoa!  Something is up with the data.  Check it"

Just like with arithmetic, you can and should use parentheses whenever there is possible ambiguity. A good general rule is to always use parentheses when mixing and and or in the same condition. That is, instead of:

if mass[i] <= 2 or mass[i] >= 5 and velocity[i] > 20:

write one of these:

if (mass[i] <= 2 or mass[i] >= 5) and velocity[i] > 20:
if mass[i] <= 2 or (mass[i] >= 5 and velocity[i] > 20):

so it is perfectly clear to a reader (and to Python) what you really mean.

Tracing Execution

What does this program print?

pressure = 71.9
if pressure 50.0:
    pressure = 25.0
elif pressure <= 50.0:
    pressure = 0.0
print(pressure)

Key Points