Overview
Teaching: 5 min Exercises: 10 minQuestions
What sorts of things frequently go wrong in programs?
How can I make my programs more robust?
Objectives
Diagnose corner cases in simple programs.
Write assertions with informative messages that test for corner cases in simple programs.
if
, print
, and sys.exit
to detect and report errors.import sys
def average(values):
if len(values) == 0:
print('Error in average: no values supplied')
sys.exit(1)
return sum(values) / len(values)
assert
to check internal correctness.assert
statement checks whether a condition is true.
if
, but signals an error instead of controlling a block of code.import sys
def average(values):
assert len(values) > 0, 'No values supplied'
return sum(values) / len(values)
def kelvin_to_celsius(k):
assert k >= 0.0, 'Temperature in Kelvin cannot be negative'
return k - 273.15
assert count_leading_zeros([]) == 0
assert count_leading_zeros([0]) == 1
assert count_leading_zeros([0, 1]) == 1
assert count_leading_zeros([0, 1, 0]) == 1
assert count_leading_zeros([0, 0, 1]) == 2
assert count_leading_zeros([1, 0]) == 0
Is This Needed?
The
os
library contains a function calledos.path.exists(path)
that returnsTrue
if the file or directory identified bypath
exists, andFalse
if it does not. A colleague of yours routinely uses it to check whether a file exists before trying to open it:... assert os.path.exists(filename), 'File {0} not found'.format(filename) reader = open(filename, 'r') ...
Should you adopt this practice? Why or why not?
Finding Corner Cases
- Under what circumstances will the following function fail with an error?
- Under what circumstances will it return something other than what the user would expect?
def find_two_smallest(values): "Find the two smallest values in a list." copy = values[:] copy.sort() return copy[:2]
Test-Driven Development.
- Explain in simple language what
run_starts
function is supposed to do.- Fill in the blanks in the function definition so that all of the assertions succeed.
- For what input(s) could different users reasonably expect this function to return different values? I.e., where could reasonable people still disagree about the function’s behavior?
- Add one or more assertions to test for each situation identified above.
def run_starts(values): if values == []: ____ result = [] previous = values[0] + 1 for v in values: if v < previous: ____ previous = ____ return result assert run_starts([]) == [] assert run_starts([1]) == [1] assert run_starts([1, 2]) == [1] assert run_starts([1, 2, 1]) == [1, 1] assert run_starts([1, 2, 3, 2, 3]) == [1, 2] assert run_starts([2, 3, 4, 0, 5, 7, 4, 6]) == [2, 0, 4]
Key Points
Fail early, fail loudly.
Can use
if
,sys.exit
to detect and report errors.Use
assert
to check internal correctness.Practice defensive programming.
Practice test-driven development.