Python Fundamentals - part 2

Presenter Notes

Overview

In this section, we will be covering

  • Passing information to your Python program
  • Decision making
  • While loops
  • Dictionaries
  • Creating functions
  • Understanding errors and exceptions
  • Defensive programming

Presenter Notes

Command line arguments

Information we may want to pass from the command line

Presenter Notes

Command line arguments

Information we may want to pass from the command line

  • file names
  • variables
  • options
  • data from other programs

Presenter Notes

Command line arguments

In the previous Python lecture, you were introduced to lists. Let's look at a special list, the command line arguments.

When you run a Python script, everything from the command line used to run the script is stored as a list in a special variable, sys.argv.


sys.argv

Presenter Notes

Let's write a short program that prints out the values in sys.argv and call it argv.py.

1import sys
2# this library contains all sorts of built in functionality in Python
3
4arguments = sys.argv
5print(arguments)

Presenter Notes

Let's write a short program that prints out the values in sys.argv and call it argv.py.

1import sys
2# this library contains all sorts of built in functionality in Python
3
4arguments = sys.argv
5print(arguments)

Now, if we run this script, what should we see?

1> python argv.py

Presenter Notes

Let's write a short program that prints out the values in sys.argv and call it argv.py.

1import sys
2# this library contains all sorts of built in functionality in Python
3
4arguments = sys.argv
5print(arguments)

Now, if we run this script, what should we see?

1> python argv.py
2["argv.py"]

Presenter Notes

What about if we want to get everything from the command line except the name of our script (presumably we already know that)?

Here is our command line call:

1> python argv.py arg1 arg2 arg3

And here is what we want our program to print:

1["arg1", "arg2", "arg3"]

Presenter Notes

Here is our command line call:

1> python argv.py arg1 arg2 arg3
2["arg1", "arg2", "arg3"]

We can use something like

1import sys
2
3print(sys.argv[1:])
4# We use the empty argument on the right side of the index
5# since we don't know how many arguments there are

Presenter Notes

Decision Making

or

How I stopped worrying and learned to love the conditional

Obviously our scripts are of limited use if they can do only one things without responding to different inputs or circumstances. Luckily, there's an app for that.

We can ask Python to check conditions to decide what code to execute or skip with a set of commands: if, else, and elif. Let's see how they work.

Presenter Notes

Conditional Statements

Let's consider the following script:

1# myscript.py
2
3num = 37
4if num > 50:
5    print("Big!")
6else:
7    print("Small.")
8print("done")

What should we see as the output?

Presenter Notes

Conditional Statements

Let's consider the following script:

1# myscript.py
2
3num = 37
4if num > 50:
5    print("Big!")
6else:
7    print("Small.")
8print("Done")

1> python myscript.py
2Small.
3Done

Presenter Notes

Conditional statements

So, what is happening? If we think of our script this way:

1num = 37             # Input value
2if num > 50:         # Conditional test, branch 1
3    print("Big!")    # 1st block of code
4else:                # Conditional test, branch 2
5    print("Small.")  # 2nd block of code
6print("Done")        # unconditioned block of code

if_flowchart

Presenter Notes

Conditional Statements

If we need more possible outcomes, we could add additional nested if statements.

 1# myscript.py
 2
 3num = 37
 4if num > 50:
 5    print("Big!")
 6else:
 7    if num < 50:
 8        print("Small.")
 9    else:
10        print("Equal")
11print("Done")

This can make for harder to read code and more difficult to follow logic.

Presenter Notes

Conditional Statements

If we wanted more nuance, we could add additional conditions using elif. This stands for "else if" but means that we don't have to keep adding nested if statements.

 1# myscript.py
 2
 3num = 37
 4if num > 50:
 5    print("Big!")
 6elif num < 50:
 7    print("Small.")
 8else:
 9    print("Equal")
10print("Done")

Note that you can have more than one elif statement but the else must be the last part of the conditional code. It is essentially the default option.

Presenter Notes

Conditional operators

There are several ways to compare values in Python.

  • == (is equal to)
  • != (is not equal to)
  • > (greater than)
  • < (less than)
  • >= (greater than or equal to)
  • <= (less than or equal to)

Presenter Notes

That's not what I meant

You can reverse the value of any boolean in Python with the keyword not

Looking at our previous script

1num = 37
2if num > 50:
3    print("Big!")
4elif not num > 50:  # This is the same logic as above, but negated
5    print("Small.")
6else:
7    print("Equal")
8print("Done")

Presenter Notes

How if evaluates the conditional argument(s)

The if command simply looks to see if the argument is true or false. This means you can pass a statement of any complexity so long as it resolves to True or False

For example:

1if 1 > 2 or 2 < 3:
2    print("True")

What do each of the parts of the conditional statement resolve as? Together?

Presenter Notes

How if evaluates the conditional argument(s)

In our example:

1if 1 > 2 or 2 < 3:
2    print("True")
  1. (1 > 2) is False
  2. (2 < 3) is True
  3. (False or True) is True

So, the code will print "True"

Presenter Notes

How if evaluates the conditional argument(s)

One nice thing is that as Python is evaluating the conditional statement, if it sees that the value will resolve to be True or False regardless of what comes next in the statement, it stops looking.

1a = []
2if len(a) > 0 and a[0] > 3:
3    print("Big")
4
5a.append(5)
6if len(a) > 0 and a[0] > 3:
7    print("Big")

This is useful in cases where a condition might result in an error if we haven't done something yet, like define a variable or try to index outside the bounds of a list or array.

Presenter Notes

Other useful conditional statements

There are several other really useful statements that evaluate to a boolean that you can use in an if statement.

  • in

This keywork lets you check if something is in a list (and other things which we will get to)

1a = [1, 2, 3]
2if 1 in a:         # This will evaluate as True
3    print("True")

Presenter Notes

Other useful conditional statements

There are several other really useful statements that evaluate to a boolean that you can use in an if statement.

  • in

This keywork lets you check if something is in a list (and other things which we will get to)

1a = [1, 2, 3]
2if 1 in a:         # This will evaluate as True
3    print("True")
  • The string method .startswith() and .endswith()

If you want to check a string's value but don't care about matching the whole string, it can be useful to just check the start or end

1a = "# header line"
2if a.startswith("#"):  # This will evaluate as True
3    print("header")
4if a.endswith("#"):    # This will evaluate as False
5    print("false")

Presenter Notes

Conditional evaluation of other data types

You can also evaluate other variables, like integers, strings, and lists as booleans.

  • Integer and decimal values less than or equal to zero evaluate as False
  • Empty lists and strings evaluate as False

Take for example the following code:

1a = [1, 2]
2if len(a) > 0:
3    print("list isn't empty")
4if not a:
5    print("list is empty")

How would the second if statement resolve?

Presenter Notes

Conditional evaluation of other data types

You can also evaluate other variables, like integers, strings, and lists as booleans.

  • Integer and decimal values less than or equal to zero evaluate as False
  • Empty lists and strings evaluate as False

Take for example the following code:

1a = [1, 2]
2if len(a) > 0:
3    print("list isn't empty")
4if not a:
5    print("list is empty")

How would the second if statement resolve?

  • a evaluates as True
  • not a evaluates to False

Presenter Notes

Conditional coding exercise

Instead of an exact match, think about how you would check for "close enough".

Try writing a conditional statement that checks whether a is within 10% of b.

Presenter Notes

Conditional coding exercise

Try writing a conditional statement that checks whether a is within 10% of b.

 1if a < b * 0.9:
 2    print("False")
 3elif a > b * 1.1:
 4    print("False")
 5else:
 6    print("True")
 7
 8if (a - b) / b >= -0.1 or (a - b) / b <= 0.1:
 9    print("True")
10else:
11    print("False")
12
13if max(a - b, b - a) / b <= 0.1:
14    print("True")
15else:
16    print("False")

Presenter Notes

The while loop

Sometimes you will need to loop through data but won't know for how long. In this case, a for loop is not appropriate. That is where the while loop comes in. It's like a for loop and if statement had a baby together.

The while loop consists for two parts.

  1. A conditional statement to test whether to execute the code block inside the loop
  2. A block of code within the loop that will eventually lead to the conditional statement evaluating to False (if not, you'll be in the loop forever)

Presenter Notes

The while loop

Here is a simple example of a while loop:

1counter = 0
2while counter < 5:
3    counter += 1
4    print(counter)

Like the if and for statements, the while statement ends with a : and the block of code inside the loop is indented.

What should the output of this script be?

Presenter Notes

The while loop

Here is a simple example of a while loop:

1counter = 0
2while counter < 5:
3    counter += 1
4    print(counter)

Like the if and for statements, the while statement ends with a : and the block of code inside the loop is indented.

What should the output of this script be?

1
2
3
4
5

Presenter Notes

The while loop

Sometimes you want to exit a while loop without getting to the beginning of the loop, or for a condition other than the one tested in the while statement. In this case, you can use the break command.

1a = 0
2while a < 10:
3    a += 1
4    if a == 4:
5        break 
6print(a)

The break will immediately exit a loop without executing any more of the code within the loop. It can also be used with for loops.

What do you think the above code would print?

Presenter Notes

The while loop

Sometimes you want to exit a while loop without getting to the beginning of the loop, or for a condition other than the one tested in the while statement. In this case, you can use the break command.

1a = 0
2while a < 10:
3    a += 1
4    if a == 4:
5        break 
6print(a)

The break will immediately exit a loop without executing any more of the code within the loop. It can also be used with for loops.

What do you think the above code would print?

4

Presenter Notes

When to use the while loop

What situations might a while loop be useful?

Presenter Notes

When to use the while loop

What situations might a while loop be useful?

  • When you have code that may take a variable number of executions that you don't know before running it

For example, if you are estimating a parameter and getting closer and closer, you could use a while loop with a stop condition of "error < something"

Presenter Notes

While exercise

Write a while loop to find the first power of 2 (2^1, 2^2, etc.) that is greater than 1000 (no cheating and using logarithms).

Presenter Notes

While exercise

Write a while loop to find the first power of 2 (2^1, 2^2, etc.) that is greater than 1000 (no cheating and using logarithms).

1power = 0
2while 2 ** power < 1000:
3    power += 1
4print(power)

And the result:

10

Presenter Notes

A new data structure - Dictionaries

So far we've seen one way to collect data together, lists. Let's look at another data structure, dicts, short for dictionaries.

Presenter Notes

A new data structure - Dictionaries

Unlike lists, entries in dictionaries are comprised of 2 parts, a "key" and a "value". To create a dictionary, we can use the function dict().

1my_dictionary = dict(
2    key1: value1,
3    key2: value2,
4    key3: value3)
5print(my_dictionary[key2])

First, notice that each entry has a key, :, and then a value. Second, just like indexing with numbers to get a specific value from a list, we use indexes with dictionaries. However, these indexes are the keys, not just position values.

Note: we can also use a short-cut notation to create/indicate that something is a dictionary.

1my_dictionary = {key1: value1,...}

Presenter Notes

Dictionaries vs. Lists

While they are both variable containers, there are several key differences between lists and dictionaries.

Lists

- Ordered, the values in a list are always in the same order
- Position indexed; to get a value from a list, you use [position] to retrieve it
- Can contain anything in each position
- The same value can appear as many times as you want in the list
- Slower to determine if a particular value is in the list

Presenter Notes

Dictionaries vs. Lists

While they are both variable containers, there are several key differences between lists and dictionaries.

Lists

- Ordered, the values in a list are always in the same order
- Position indexed; to get a value from a list, you use [position] to retrieve it
- Can contain anything in each position
- The same value can appear as many times as you want in the list
- Slower to determine if a particular value is in the list

Dictionaries

- Unordered, think of your key-value pairs as jumbled up in a bag
- Key-indexed; to get a value from a dictionary, you use [key] to retrieve it
- Can contain anything as a value
- Keys can only be unchangable (immutable in the comp sci lingo).
- Can contain the same value as many times as you want
- Each key must be unique
- Very fast to check if a key exists in a dictionary and get its value

Presenter Notes

Dictionaries vs. Lists

A practical exercise

Presenter Notes

Dictionary Uses

What do we need another data structure for? What uses might a dictionary have that a list would be bad at?

Presenter Notes

Dictionary Uses

What do we need another data structure for? What uses might a dictionary have that a list would be bad at?

  • Counting how many times we've seen something
  • Being able to quickly check if a value is in our data structure
  • Mapping from one set of values to another (think translating from one language to another)
  • Keeping track of data where the order will be inconsistent or some values may not appear

Presenter Notes

A note on immutables

Immutable means "can't be changed". In Python, this means that a variable is immutable if changing its value will overwrite it with the new value.

Everything we've looked at so far is immutable, except lists and dictionaries. Even strings are recreated anytime they changed.

So, things that can be used as dictionary keys (because they're immutable):

  • Ints
  • Bools
  • Floats
  • Strings
  • Tuples

Wait, what's a tuple??!?!?

Presenter Notes

Tuples

A tuple is like a list that's been frozen. Once created, it can't be changed. You can make a list into a tuple with the function tuple(). You can also create a tuple directly with the function or the short-hand (a, b, c, ...).

1a = [1, 2, 3]    # A list, so mutable
2b = tuple(a)     # A tuple, so immutable
3c = (1, 2, 3)    # We can also create tuples directly
4d = {b: a, c: a} # Because b and c are immutable, they can be dictionary keys

Presenter Notes

A Dictionary exercise

Write a script that will read in a file and create a list of values for "Male" and one for "Female". The file format is:

Female  4
Female  9
Male    1
Female  11
Male    3
...

The first value in each line will always be "Male" or "Female". Try to write the script without using if.

Presenter Notes

A Dictionary exercise

Write a script that will read in a file and create a list of values for "Male" and one for "Female".

1import sys
2
3fs = open(sys.argv[1])
4data = {"Male": [], "Female": []}
5for line in fs:
6    line = line.rstrip().split()
7    data[line[0]].append(int(line[1]))

Presenter Notes

Accessing data in a dictionary

So far, we've seen one way to get values from a dictionary, indexing with the key for the desired value. But what if we want to see everything in a dictionary? Like stepping through a list item by item, there are similar options for iterating through a dictionary.

Presenter Notes

Accessing data in a dictionary

So far, we've seen one way to get values from a dictionary, indexing with the key for the desired value. But what if we want to see everything in a dictionary? Like stepping through a list item by item, there are similar options for iterating through a dictionary.

By keys

We can essentially get a a list of keys to step through with the dictionary method .keys()

1mydict = {1: 'a', 2: 'b', 3: 'c'}
2for key in mydict.keys():
3    print(key, mydict[key])

1 a
2 b
3 c

Presenter Notes

Accessing data in a dictionary

So far, we've seen one way to get values from a dictionary, indexing with the key for the desired value. But what if we want to see everything in a dictionary? Like stepping through a list item by item, there are similar options for iterating through a dictionary.

By keys

We can essentially get a a list of keys to step through with the dictionary method .keys()

1mydict = {1: 'a', 2: 'b', 3: 'c'}
2for key in mydict.keys():
3    print(key, mydict[key])

Note: I say essentially as mydict.keys() creates an iterator, not a list. The only time this distinction is important is if you want to get a list of the keys, in which case you need to explicityly convert it into a list

keys = list(mydict.keys())

Presenter Notes

Accessing data in a dictionary

So far, we've seen one way to get values from a dictionary, indexing with the key for the desired value. But what if we want to see everything in a dictionary? Like stepping through a list item by item, there are similar options for iterating through a dictionary.

By values

We can essentially get a a list of values to step through much like keys with the dictionary method .values()

1mydict = {1: 'a', 2: 'b', 3: 'c'}
2for value in mydict.values():
3    print(value)

a
b
c

Like with mydict.keys(), you will need explicitly convert this to a list if you want a list of values.

Presenter Notes

Accessing data in a dictionary

So far, we've seen one way to get values from a dictionary, indexing with the key for the desired value. But what if we want to see everything in a dictionary? Like stepping through a list item by item, there are similar options for iterating through a dictionary.

By keys and values

We can step through with the dictionary getting key-value pairs with the method .items(). A tuple will be returned with the pair which we can unpack in the for loop line.

1mydict = {1: 'a', 2: 'b', 3: 'c'}
2for key, value in mydict.items():
3    print(key, value)

1 a
2 b
3 c

Presenter Notes

Defining functions

So far, you have used a variety of functions. Any time you use something in your script immediately followed by parentheses (e.g. print() ), you are using a function.

Let's define our own function. This requires 3 parts:

  1. Specifying the name of the function and what parameters should be passed to it, if any
  2. What code does the function execute
  3. What information does the function return after running

1# Lets define the function max()
2def max(a, b):
3    if a >= b:
4        val = a
5    else:
6        val = b
7    return val

Presenter Notes

Defining functions

  1. Specifying the name of the function and what parameters should be passed to it, if any

1def max(a, b):
  • The def tells Python you are about to define a function
  • max is the name of the function (you can use alphanumeric characters and underscores)
  • Inside the parentheses, you give a list of expected parameters. These will be the variable names within the function code
  • The : indicates that the block of code to follow is the body of the function

Presenter Notes

Defining functions

  1. What code does the function execute

1def max(a, b):
2    if a >= b:
3        val = a
4    else:
5        val = b
  • Just like if statements and for loops, the body of the function needs to be indented to indicate which code belongs to the function
  • Inside the function you can use the variables that you defined as parameters passed to the function
  • You can also create new variables inside the function, but these disappear when the function finishes

Presenter Notes

Defining functions

  1. What information does the function return after running

1return val
  • A function can pass back any kind of information you want using the return keyword
  • You can return multiple things, which will be turned into a list automatically
  • If you don't have return, the function will end at the end of the code block and return None, Python's empty value

Presenter Notes

Defining functions

You can even include multiple return commands, with the function ending as soon as it executes one of them.

For example we could have written the function:

1# Lets define the function max()
2def max(a, b):
3    if a >= b:
4        return a
5    else:
6        return b

This is a little cleaner, but in this case it makes no difference in the way the function acts.

Presenter Notes

Defining functions

One extension to function parameters that may be useful to know is the keyword / default parameters. Sometimes we don't need to pass a value for every parameter.

1def plot_my_data(X, Y, title="", xlabel=""):
2    fig = plt.figure()
3    fig.plot(X, Y)
4    if title:
5        fig.set_title(title)
6    if xlabel:
7        fig.set_xlabel(xlabel)
8    plt.show()

In this case, we can pass 2, 4, or 4 arguments. There are only a couple rules:

  1. X and Y must be passed in that order
  2. title can be passed as the third argument or by using its parameter keyword
  3. xlabel can be passed as the fourth argument or by using its parameter keyword
  4. All unnamed (positional) parameters have to come before keyword parameters

Presenter Notes

Defining functions

So, given our function plot_my_data(X, Y, title="", xlabel=""), here are several ways we could call this function:

1fig = plot_my_data(X, Y)
2
3fig = plot_my_data(X, Y, "myplot", "x-axis")
4
5fig = plot_my_data(X, Y, title="myplot")
6
7fig = plot_my_data(X, Y, xlabel="x-axis")

Presenter Notes

Function exercise

In order to practice what you've just learned, try writing your own function to find the area of a rectangle. It should:

  1. Take in height and width as parameters
  2. If either parameter is omitted, that parameter should have a default value of 1

Presenter Notes

Function exercise

Hopefully it was clear that in order to have a default value, each parameter also needed a name. A simple solution would be

1def area(height=1, width=1):
2    return height * width
3
4A = area(3, width=3)
5print(A)  # should print 9

Notice that I used clear names for my function and my parameters. It makes reading and understanding it MUCH easier, especially when you return to your own code days or weeks later.

Presenter Notes

Why use functions

What advantage is there to putting your code in a function?

Presenter Notes

Why use functions

What advantage is there to putting your code in a function?

  1. Your code will be reusable anywhere in your script (or even outside your script)
  2. It makes your script easier to read when each task is defined as its own function
  3. It makes your code more flexible. Consider a function that accepts another function as a parameter

Presenter Notes

Why use functions

What advantage is there to putting your code in a function?

  1. Your code will be reusable anywhere in your script
  2. It makes your script easier to read when each task is defined as its own function
  3. It makes your code more flexible. Consider a function that accepts another function as a parameter

1def apply_to_list(data, func):
2    result = data[0]
3    for i in range(1, len(data)):
4        result = func(result, data[i])
5    return result

We could use this to sum a list, find the product of the list, find the maximum value of the list, etc.

1from math import prod # We need to import the function for finding products
2data = [1, 2, 3, 4, 5]
3print(apply_to_list(data, sum)) # prints 15
4print(apply_to_list(data, prod)) # prints 120
5print(apply_to_list(data, max)) # prints 5

Presenter Notes

Understanding errors and exceptions

It is inevitable that you will have bugs in your code. Luckily Python gives detailed information to help you understand what wrong.

Take the following code as an example:

1def sum_data(a):
2    return a + b
3
4def run_test():
5    data = 120
6    result = sum_data(data)
7    return result
8
9run_test()

What will happen when we run this?

Presenter Notes

Understanding errors and exceptions

1def sum_data(a):
2    return a + b
3
4def run_test():
5    data = 120
6    result = sum_data(data)
7    return result
8
9run_test()

We get the output:

1Traceback (most recent call last):
2  File ".../error.py", line 9, in <module>
3    run_test()
4  File ".../error.py", line 6, in run_test
5    result = sum_data(data)
6  File ".../error.py", line 2, in sum_data
7    return a + b
8NameError: name 'b' is not defined

Presenter Notes

Understanding a Traceback

1Traceback (most recent call last):
2  File ".../error.py", line 9, in <module>
3    run_test()
4  File ".../error.py", line 6, in run_test
5    result = sum_data(data)
6  File ".../error.py", line 2, in sum_data
7    return a + b
8NameError: name 'b' is not defined

The error Traceback is a map of the chain of events that led to the error in your code. It may be easier to read it from bottom to top to start with.

Presenter Notes

Understanding a Traceback

1Traceback (most recent call last):
2  File ".../error.py", line 9, in <module>
3    run_test()
4  File ".../error.py", line 6, in run_test
5    result = sum_data(data)
6  File ".../error.py", line 2, in sum_data
7    return a + b
8NameError: name 'b' is not defined
  1. NameError: name 'b' is not defined

This is the specific kind of error, or "Exception" that occurred. There are several defined exceptions that are built in to Python, but some external libraries define their own.

In this case, Python is telling us that we used a name that the currently executing code doesn't know.

Presenter Notes

Understanding a Traceback

1Traceback (most recent call last):
2  File ".../error.py", line 9, in <module>
3    run_test()
4  File ".../error.py", line 6, in run_test
5    result = sum_data(data)
6  File ".../error.py", line 2, in sum_data
7    return a + b
8NameError: name 'b' is not defined
  1. NameError: name 'b' is not defined
  2. File ".../error.py", line 2, in sum_data return a + b

This line is telling us what function called the code that failed, which script it was in, and at which line.

This can be important when you are using code that isn't yours (built in or imported) and the error occurs in that external code.

Presenter Notes

Understanding a Traceback

1Traceback (most recent call last):
2  File ".../error.py", line 9, in <module>
3    run_test()
4  File ".../error.py", line 6, in run_test
5    result = sum_data(data)
6  File ".../error.py", line 2, in sum_data
7    return a + b
8NameError: name 'b' is not defined
  1. NameError: name 'b' is not defined
  2. File ".../error.py", line 2, in sum_data return a + b
  3. File ".../error.py", line 6, in run_test result = sum_data(data)
  4. File ".../error.py", line 9, in run_test()

These last two sets of lines are moving up the error chain, showing the path of function calls that ultimately led to the flawed function being called.

Presenter Notes

Python Exceptions

As mentioned earlier, there are a variety of built in exceptions, each with a well defined use case.

  • ImportError This is used when an import statement fails
  • IndexError This occurs when you try to get an item outside the range of a list or array (e.g. [1,2][3])
  • KeyboardInterrupt This will show up anytime you force your code to quit with ctrl-d
  • IndentationError This indicates that you have indentation that doesn't match what's expected based on if statements, for loops, etc.
  • SyntaxError This tells you that somewhere there is code that isn't properly using Python syntax (e.g. if a > b , missing :)
  • TypeError This error means that one data type was expected but a different one was given (a function takes an integer but is given a list)
  • RuntimeError This is a catch-all error if Python doesn't have a better definition for what went wrong

There are many more exceptions, most which you should never encounter, some that you might but are self-explanatory, and a couple that we will see shortly.

Presenter Notes

Understanding errors and exceptions

Given the code and traceback, how could you fix our script?

1def sum_data(a):
2    return a + b
3
4def run_test():
5    data = 120
6    result = sum_data(data)
7    return result
8
9run_test()

1Traceback (most recent call last):
2  File ".../error.py", line 9, in <module>
3    run_test()
4  File ".../error.py", line 6, in run_test
5    result = sum_data(data)
6  File ".../error.py", line 2, in sum_data
7    return a + b
8NameError: name 'b' is not defined

Presenter Notes

Understanding errors and exceptions

Some possible answers

1def sum_data(a, b):
2    return a + b
3
4def run_test():
5    data = 120
6    data2 = 5
7    result = sum_data(data, data2)
8    return result

1def sum_data(a, b=0):
2    return a + b

Presenter Notes

Some common errors to avoid

There are some common errors that even seasoned programmers still make, Be on the lookout in your own code.

  • Mixing tabs and spaces. Python hates this. Be kind and always use spaces (the convention is 4 spaces per indent)
  • Forgetting to put strings in quotations. For example, print(hello) will give you a NameError unless you actually defined a variable called hello
  • Forgetting the colon after a statement like if, for, def function(), and a few others you haven't seen yet
  • Mixing up = and ==. This is especially tricky to find when you try to assign a value to a variable but use ==, since it won't give you an error.

Presenter Notes

Debugging excercise

Create a script with the following code:

1for number in range(10):
2    # use a if the number is a multiple of 3, otherwise use b
3    if (Number % 3) == 0:
4        message = message + a
5    else:
6        message = message + 'b'
7print(message)

Correct errors until you can get it to successfully run.

Presenter Notes

Debugging excercise

1for number in range(10):
2    # use a if the number is a multiple of 3, otherwise use b
3    if (Number % 3) == 0:     # Number isn't a variable we have defined
4        message = message + a # We never defined message. It should be message = ""
5    else:                     # Also, we need the letter 'a', not the variable a
6        message = message + 'b'
7print(message)

Presenter Notes

Defensive programming

One of the biggest mistakes you can make in programming is assuming people (including you) will use your code correctly. Instead, it's a good idea to make your code idiot-proof (so to speak).

Presenter Notes

Defensive programming

One of the biggest mistakes you can make in programming is assuming people (including you) will use your code correctly. Instead, it's a good idea to make your code idiot-proof (so to speak).

One way to do this is with the assert statement. This statements says "this should be True" and will throw an AssertionError if the result is False. This let's you check that things meet your expectations before moving on.

Presenter Notes

Assertions

Consider the following function:

1def area(a, b):
2    return a * b

This is completely straigth-forward and works, if used properly. But how could it be used incorrectly?

Presenter Notes

Assertions

Consider the following function:

1def area(a, b):
2    return a * b

This is completely straigth-forward and works, if used properly. But how could it be used incorrectly?

  • Negative values
  • Values other than integers or floats

Presenter Notes

Assertions

So let's make the function more robust with assert statements.

1def area(a, b):
2    assert type(a) in [int, float] and type(b) in [int, float]
3    assert a >= 0 and b >= 0
4    return a * b

Now, if we use this function improperly, our assertions will catch it rather than getting unexpected behavior.

1area(1, [2])

1Traceback (most recent call last):
2  File "./area.py", line 6, in <module>
3    area(1, [2])
4  File "./area.py", line 2, in area
5    assert type(a) in [int, float] and type(b) in [int, float]
6AssertionError

Presenter Notes

Assertions

We can also add more informative messages to the assert statements

1def area(a, b):
2    assert type(a) in [int, float] and type(b) in [int, float], "a and b need to be numbers"
3    assert a >= 0 and b >= 0, "must not be negative"
4    return a * b

Now when we misuse the function, we get a clear reason why it doesn't work

1area(1, [2])

1Traceback (most recent call last):
2  File "./area.py", line 6, in <module>
3    area(1, [2])
4  File "./area.py", line 2, in area
5    assert type(a) in [int, float] and type(b) in [int, float], "a and b need to be numbers"
6AssertionError: a and b need to be numbers

Presenter Notes

Testing code

Finally, how do you test your code? First, always try to follow the rule "Fail fast". Second, check things at each step, don't wait for a finished script to find a giant pile of errors.

  • Make a small test dataset, it will run fast and you know what should happen
  • Check each step to make sure it does what you think it should
  • Add comments! Let future you know what current you is thinking
  • If you get an error, somebody else has also gotten that error and asked for help. Use google, stackoverflow.com, etc. to look for answers

Presenter Notes

Questions?

Presenter Notes