Class 9C: Programming in Python II#

We will begin soon!

Firas Moosvi
from IPython.display import IFrame
from IPython.display import Markdown

# Additional styling ; should be moved into helpers
from IPython.display import display, HTML

HTML("<style>{}</style>".format(open("rise.css").read()))

Programming in Python I#

In this class, we go through a notebook by a former colleague, Dr. Mike Gelbart, option co-director of the UBC-Vancouver MDS program.

If you prefer, you can also watch his recording of the same material.

Announcements#

  • Here is the plan for the next couple of weeks:

  • Week 10 (next week):

    • Monday March 13th 10:00 AM - 10:40 AM Review Class

    • Monday March 13th 10:40 AM - 12:00 PM Project Feedback

    • Wednesday March 15th 10:00 AM - 11:00 AM Project Feedback

    • Friday March 15th 10:00 AM - 11:00 AM Project Feedback

  • Week 11 (two weeks from now):

    • Monday March 20th 10:00 AM - 11:00 AM Class on Zoom (I’m away)

    • Monday March 20th 11:00 AM - 12:00 PM Student hours on Zoom (I’m away)

    • Wednesday March 22nd 10:00 AM - 11:00 AM Class on Zoom (I’m away)

    • Friday March 24th 10:00 AM - 11:00 AM Class is Cancelled

  • Week 12 (three weeks from now):

    • Return to regularly scheduled programming

Short demo on GitHub Issues and Project Feedback (5 mins)#

Class Outline#

  • Short demo on GitHub Issues and Project Feedback (5 mins)

  • Assert Statements (10 mins)

  • Try/Except clauses (10 mins)

  • Unit tests, corner cases (15 mins)

  • Multiple return values (15 mins)

Attribution#

Assert statements#

test = ["1", "2", 3, 4, 5]
ans = [1, 2, 3, 4, 5]

assert (test == ans), "Helium baloons fly"  # Left side isn't the same as the right side
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
Cell In[2], line 4
      1 test = ["1", "2", 3, 4, 5]
      2 ans = [1, 2, 3, 4, 5]
----> 4 assert (test == ans), "Helium baloons fly"  # Left side isn't the same as the right side

AssertionError: Helium baloons fly
# method 1
for i, t in enumerate(test):
    #print(i)
    #print(t)
    test[i] = int(t)

test

assert test == ans, "Failure! Left != Right"
# method 2

assert [int(t) for t in test] == ans, "Failure! Left != Right"

Difference between try/except and assert#

import numpy as np

def make_larger(n, denominator):
    """Makes the number larger"""

    assert denominator != 0, "You provided 0 as the denominator - you are a terrible person"
    assert (type(n) == int), "You did not provide an integer as an input"
    
    ret = np.nan
#     ret = n**2 / denominator
    try:
        ret = n**2 / denominator
    except ZeroDivisionError as e:
        print("You provided 0 as the denominator - you are a terrible person")
        raise e
    except TypeError as e:
        print("You should make sure your inputs are actually numbers, I'll try to fix this")
        ret = int(n)**2 / denominator
#     except:
#         print("there was an error in your code")
    finally:
        print("Don't worry, you are the best coder in the history of the coders")
        return ret
num1 = "5"
num2 = 2

make_larger(num1, num2)
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
Input In [51], in <cell line: 4>()
      1 num1 = "5"
      2 num2 = 2
----> 4 make_larger(num1, num2)

Input In [50], in make_larger(n, denominator)
      4     """Makes the number larger"""
      6     assert denominator != 0, "You provided 0 as the denominator - you are a terrible person"
----> 7     assert (type(n) == int), "You did not provide an integer as an input"
      9     ret = np.nan
     10 #     ret = n**2 / denominator

AssertionError: You did not provide an integer as an input

Try and except blocks (10 minutes)#

g = [1, 2, 3, 4, 5, 6, 7]

# g[8] = 'Hello' # This way will cause an error

# this is a more graceful way of dealing with errors
try:
    g[4] = "Hello"
    #h = 7 / 0
except IndexError:
    print(
        f"your list is less than 8 elements long, your list is actually {len(g)} elements"
    )
# except ZeroDivisionError:
#     print("stop doing stupid things")
# except:
#     print('there is some sort of error in your code')
len(g)
7

Systematic Program Design#

A systematic approach to program design is a general set of steps to follow when writing programs. Our approach includes:

  1. Write a stub: a function that does nothing but accept all input parameters and return the correct datatype.

  2. Write tests to satisfy the design specifications.

  3. Outline the program with pseudo-code.

  4. Write code and test frequently.

  5. Write documentation.

The key point: write tests BEFORE you write code.

  • You do not have to do this in MDS, but you may find it surprisingly helpful.

  • Often writing tests helps you think through what you are trying to accomplish.

  • It’s best to have that clear before you write the actual code.

Testing woes - false positives#

  • Just because all your tests pass, this does not mean your program is correct!!

  • This happens all the time. How to deal with it?

    • Write a lot of tests!

    • Don’t be overconfident, even after writing a lot of tests!

# Write a function to take in a list and return the median value

# Step 1
def sample_median2(x):
    
    # Add code here
    
    return median_value

# Step 2
def sample_median2(x):
    
    # I'm going to sort the list
    
    # I'm going to get the length of the list 
    
    # I'm going get the middle number of the length of the list (//)
    
    # I'm going to access the median value 
    
    return median_value

# Step 3
def sample_median2(x):
    
    # I'm going to sort the list
    x_sorted = sorted(x)
    
    # I'm going to get the length of the list
    length = len(x)
    
    # I'm going get the middle number of the length of the list (//)
    middle = length // 2
    
    # I'm going to access the median value 
    median_value = x_sorted[middle]
    
    return median_value

# Step 4
def sample_median2(x):
    
    assert type(x) == list, "You didn't pass me a list!"
    
    # I'm going to sort the list
    x_sorted = sorted(x)
    
    # I'm going to get the length of the list
    length = len(x)
    
    # I'm going get the middle number of the length of the list (//)
    middle = length // 2
    
    # I'm going to access the median value 
    median_value = x_sorted[middle]
    
    return median_value

# Step 5
def sample_median2(x):
    
    assert type(x) == list, "You didn't pass me a list!"
    
    # I'm going to sort the list
    x_sorted = sorted(x)
    
    # I'm going to get the length of the list
    length = len(x)
    
    # I'm going get the middle number of the length of the list (//)
    middle = length // 2
    
    # I'm going to access the median value 
    median_value = x_sorted[middle]
    
    return median_value

assert sample_median2([1, 2, 3, 4, 5]) == 3
assert sample_median2([0, 0, 0, 0]) == 0
sample_median([5,4,2,1,3])
3
sample_median2(5)
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
Input In [65], in <cell line: 1>()
----> 1 sample_median2(5)

Input In [62], in sample_median2(x)
     41 def sample_median2(x):
---> 43     assert type(x) == list, "You didn't pass me a list!"
     45     # I'm going to sort the list
     46     x_sorted = sorted(x)

AssertionError: You didn't pass me a list!
def sample_median(x):
    """Finds the median of a list of numbers."""
    x_sorted = sorted(x)
    return x_sorted[len(x_sorted) // 2]


assert sample_median([1, 2, 3, 4, 5]) == 3
assert sample_median([0, 0, 0, 0]) == 0

Looks good? … ?






assert sample_median([1, 2, 3, 4]) == 2.5






assert sample_median([1, 3, 2]) == 2

Testing woes - false negatives#

  • It can also happen, though more rarely, that your tests fail but your program is correct.

  • This means there is something wrong with your test.

  • For example, in the autograding for lab1 this happened to some people, because of tiny roundoff errors.

Corner cases#

  • A corner case is an input that is reasonable but a bit unusual, and may trip up your code.

  • For example, taking the median of an empty list, or a list with only one element.

  • Often it is desirable to add test cases to address corner cases.

assert sample_median([1]) == 1
  • In this case the code worked with no extra effort, but sometimes we need if statements to handle the weird cases.

  • Sometimes we want the code to throw an error (e.g. median of an empty list); more on this later.

Multiple return values (5 mins)#

  • In most (all?) programming languages I’ve seen, functions can only return one thing.

  • That is technically true in Python, but there is a “workaround”, which is to return a tuple (or similarly, a dictionary).

# not good from a design perspective!
def sum_and_product(x, y):
    return (x + y, x * y)
sum_and_product(5, 6)

In some cases in Python, the parentheses can be omitted:

def sum_and_product(x, y):
    return x + y, x * y
sum_and_product(5, 6)

It is common to store these in separate variables, so it really feels like the function is returning multiple values:

s, p = sum_and_product(5, 6)
s
p
  • Question: is this good function design.

  • Answer: usually not, but sometimes.

  • You will encounter this in some Python packages.

That’s it for today!

See you on Thursday

Demo of writing code#

Task: GIve me a string, find the longest palindrome in the strong

  • Reverse a string

  • Find the largest number in a given list, if it’s even multiply it by 10, if it’s odd, multiply it by a 1000

def largest_num_operation(provided_list):

    result = provided_list

    return result
def largest_num_operation(provided_list):

    result = provided_list

    return result


assert (
    largest_num_operation([1, 2, 3, 4, 5, 6]) == 60
), "Your function does not work for even numbers!"
assert (
    largest_num_operation([1, 2, 3, 4, 5]) == 5000
), "Your function does not work for odd numbers!"
def largest_num_operation(provided_list):

    result = provided_list

    # 1. Find the largest number in the list and store it
    # largest_val = max(provided_list)
    # 2. I check to see if the number is odd or even
    # %2 ==0 (even)
    # %2 !=0 (odd)
    # 3. If it's odd I multiply by 1000
    # x 1000
    # 4. If it's even, I multiply by 10
    # x 10

    return result


assert (
    largest_num_operation([1, 2, 3, 4, 5, 6]) == 60
), "Your function does not work for even numbers!"
assert (
    largest_num_operation([1, 2, 3, 4, 5]) == 5000
), "Your function does not work for odd numbers!"
def largest_num_operation(provided_list):
    
    result = provided_list
    
    # 1. Find the largest number in the list and store it
        # largest_val = max(provided_list)
        
    largest_val = max(provided_list)
        
    # 2. I check to see if the number is odd or even
        # %2 ==0 (even)
        # %2 !=0 (odd)
        
    if largest_val % 2 == 0:
        # x 1000
    elif largest_val:
        # x 10
        
    return result
    # 3. If it's odd I multiply by 1000
        # x 1000
    # 4. If it's even, I multiply by 10
        # x 10
    
    return result

assert largest_num_operation([1,2,3,4,5,6]) == 60, "Your function does not work for even numbers!"
assert largest_num_operation([1,2,3,4,5]) == 5000, "Your function does not work for odd numbers!"
def largest_num_operation(provided_list):

    largest_val = max(provided_list)

    if largest_val == 0:
        print("please put in a more useful number, instead of 0")
        return

    if largest_val % 2 == 0:
        result = largest_val * 10
    elif largest_val:
        result = largest_val * 1000
    return result


assert (
    largest_num_operation([1, 2, 3, 4, 5, 6]) == 60
), "Your function does not work for even numbers!"
assert (
    largest_num_operation([1, 2, 3, 4, 5]) == 5000
), "Your function does not work for odd numbers!"
largest_num_operation([-1, -2, -3, -4, -5, 0])
test_list = [1, 2, 3, 4, 5, 6]

max(test_list)
  • Write a stub: a function that does nothing but accept all input parameters and return the correct datatype.

  • Write tests to satisfy the design specifications.

  • Outline the program with pseudo-code.

  • Write code and test frequently.

  • Write documentation.