Newer
Older
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Defensive Programming\n",
"\n",
"Defensive programming describes concepts and approaches we can use to ensure that...\n",
"* Code does what we think it does (i.e. is free of bugs)\n",
"* Our programs fail when they should...\n",
"* ... and do so with informative error messages (throwback to last session)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's say we want a function that takes in a list and computes the mean of all elements in that list"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Assert statement\n",
"\n",
"The simplest tool we have to ensure that our functions are doing what we intend them to do are assert statements"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"execution_count": null,
"metadata": {},
"outputs": [],
"get_sum_of_numbers([\"a\", \"b\", \"c\", \"d\", \"e\"])"
"def get_sum_of_numbers(alist):\n",
" assert all([isinstance(element, int) for element in alist]), \"Not all elements are integers\"\n",
"execution_count": null,
"metadata": {},
"outputs": [],
"execution_count": null,
"metadata": {},
"outputs": [],
"get_sum_of_numbers([\"a\", \"b\", \"c\", \"d\", \"e\"])"
"execution_count": null,
"metadata": {},
"outputs": [],
"metadata": {},
"outputs": [],
"source": [
"import numbers\n",
"def get_sum_of_numbers(alist):\n",
" assert all([isinstance(element, numbers.Number) for element in alist]), \"Not all elements are Numbers\"\n",
" tmp = tmp + element\n",
" return(tmp)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"execution_count": null,
"metadata": {},
"outputs": [],
"metadata": {},
"outputs": [],
"source": [
" assert len(alist) > 0, \"List is empty\"\n",
" assert all([isinstance(element, numbers.Number) for element in alist]), \"Not all elements are Numbers\"\n",
" tmp = tmp + element\n",
" return(tmp)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Preconditions, postconditions and invariants\n",
"\n",
"Assertions can be grouped into:\n",
"* Preconditions, which should be true the computional part of a function\n",
"* Postconditions, which should be true afterwards\n",
"* (Invariants, which should be true anywhere)"
]
},
{
"cell_type": "code",
"metadata": {},
"outputs": [],
"source": [
"import numbers\n",
"def get_sum_of_numbers(alist):\n",
" assert len(alist) > 0, \"List is empty\"\n",
" assert all([isinstance(element, numbers.Number) for element in alist]), \"Not all elements are Numbers\"\n",
"execution_count": null,
"metadata": {},
"outputs": [],
"metadata": {},
"outputs": [],
"source": [
"import numbers\n",
"def get_sum_of_numbers(alist):\n",
" assert len(alist) > 0, \"List is empty\"\n",
" assert all([isinstance(element, numbers.Number) for element in alist]), \"Not all elements are Numbers\"\n",
" tmp = tmp + element\n",
" tmp = 'Your code has been visited by an evil spirit!'\n",
" return(tmp)"
"execution_count": null,
"metadata": {},
"outputs": [],
"metadata": {},
"outputs": [],
"source": [
"import numbers\n",
"def get_sum_of_numbers(alist):\n",
" assert len(alist) > 0, \"List is empty\"\n",
" assert all([isinstance(element, numbers.Number) for element in alist]), \"Not all elements are Numbers\"\n",
" tmp = tmp + element\n",
" tmp = 'Your code has been visited by an evil spirit!'\n",
" # Postcondition\n",
" assert isinstance(tmp, numbers.Number), \"Your sum isn't numerical...\"\n",
"execution_count": null,
"metadata": {},
"outputs": [],
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Test-Driven Development\n",
"\n",
"Some programmers write code and then test that code somehow to be confident enough that it's bug free. \n",
"\n",
"Other programms create tests and then write code that has to pass these tests in an iterative manner. This approach is called test-driven development. "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We want to write a function that calculates the largest overlap between numerical ranges. Each numerical range is a two-element tuple specifying the upper and lower boundaries."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
""
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Single range\n",
"assert range_overlap([ (0.0, 1.0) ]) == (0.0, 1.0)\n",
"# Two overlapping ranges\n",
"assert range_overlap([ (2.0, 3.0), (2.0, 4.0) ]) == (2.0, 3.0)\n",
"# Three overlapping ranges\n",
"assert range_overlap([ (0.0, 1.0), (0.0, 2.0), (-1.0, 1.0) ]) == (0.0, 1.0)\n",
"\n",
"# What else is missing?\n",
"# Twice the same range\n",
"assert range_overlap([(0, 2), (0, 2)]) == (0.0, 2.0)\n",
"# Two non-overlapping ranges. What is the desired output? (0, 0)? None?\n",
"assert range_overlap([ (0.1, 1.0), (3.0, 4.0) ]) == None\n",
"# More than two non-overlapping ranges. See above.\n",
"assert range_overlap([ (0.1, 1.0), (3.0, 4.0), (10, 12) ]) == None\n",
"assert range_overlap([(0, 2), (2, 4)]) == None\n",
"# empty input?\n",
"assert range_overlap([]) == None"
]
},
{
"cell_type": "code",
"metadata": {},
"outputs": [],
"source": [
"def range_overlap(ranges):\n",
" max_left = ranges[0][0]\n",
" min_right = ranges[0][1]\n",
" # ranges[2:] returns an empty list if len(ranges) == 1 and nothing is being looped over.\n",
" for (left, right) in ranges[1:]:\n",
" max_left = max(max_left, left)\n",
" min_right = min(min_right, right)\n",
" return (max_left, min_right)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Single range\n",
"assert range_overlap([ (0.0, 1.0) ]) == (0.0, 1.0)\n",
"# Two overlapping ranges\n",
"assert range_overlap([ (2.0, 3.0), (2.0, 4.0) ]) == (2.0, 3.0)\n",
"# Three overlapping ranges\n",
"assert range_overlap([ (0.0, 1.0), (0.0, 2.0), (-1.0, 1.0) ]) == (0.0, 1.0)\n",
"\n",
"# What else is missing?\n",
"# Twice the same range\n",
"assert range_overlap([(0, 2), (0, 2)]) == (0.0, 2.0)\n",
"# Two non-overlapping ranges. What is the desired output? (0, 0)? None?\n",
"assert range_overlap([ (0.1, 1.0), (3.0, 4.0) ]) == None\n",
"# More than two non-overlapping ranges. See above.\n",
"assert range_overlap([ (0.1, 1.0), (3.0, 4.0), (10, 12) ]) == None\n",
"# Two touching intervals\n",
"assert range_overlap([(0, 2), (2, 4)]) == None\n",
"# empty input?\n",
"assert range_overlap([]) == None"
]
},
{
"cell_type": "code",
"metadata": {},
"outputs": [],
"source": [
"def range_overlap(ranges):\n",
" max_left = ranges[0][0]\n",
" min_right = ranges[0][1]\n",
" # ranges[2:] returns an empty list if len(ranges) == 0 and nothing is being looped over.\n",
" for (left, right) in ranges[1:]:\n",
" max_left = max(max_left, left)\n",
" min_right = min(min_right, right)\n",
" if max_left > min_right:\n",
" return(None)\n",
" return (max_left, min_right)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Single range\n",
"assert range_overlap([ (0.0, 1.0) ]) == (0.0, 1.0)\n",
"# Two overlapping ranges\n",
"assert range_overlap([ (2.0, 3.0), (2.0, 4.0) ]) == (2.0, 3.0)\n",
"# Three overlapping ranges\n",
"assert range_overlap([ (0.0, 1.0), (0.0, 2.0), (-1.0, 1.0) ]) == (0.0, 1.0)\n",
"\n",
"# What else is missing?\n",
"# Twice the same range\n",
"assert range_overlap([(0, 2), (0, 2)]) == (0.0, 2.0)\n",
"# Two non-overlapping ranges. What is the desired output? (0, 0)? None?\n",
"assert range_overlap([ (0.1, 1.0), (3.0, 4.0) ]) == None\n",
"# More than two non-overlapping ranges. See above.\n",
"assert range_overlap([ (0.1, 1.0), (3.0, 4.0), (10, 12) ]) == None\n",
"# Two touching intervals\n",
"assert range_overlap([(0, 2), (2, 4)]) == None\n",
"# empty input?\n",
"assert range_overlap([]) == None"
]
},
{
"cell_type": "code",
"metadata": {},
"outputs": [],
"source": [
"def range_overlap(ranges):\n",
" max_left = ranges[0][0]\n",
" min_right = ranges[0][1]\n",
" # ranges[2:] returns an empty list if len(ranges) == 1 and nothing is being looped over.\n",
" for (left, right) in ranges[1:]:\n",
" max_left = max(max_left, left)\n",
" min_right = min(min_right, right)\n",
" if max_left >= min_right:\n",
" return(None)\n",
" return (max_left, min_right)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Single range\n",
"assert range_overlap([ (0.0, 1.0) ]) == (0.0, 1.0)\n",
"# Two overlapping ranges\n",
"assert range_overlap([ (2.0, 3.0), (2.0, 4.0) ]) == (2.0, 3.0)\n",
"# Three overlapping ranges\n",
"assert range_overlap([ (0.0, 1.0), (0.0, 2.0), (-1.0, 1.0) ]) == (0.0, 1.0)\n",
"\n",
"# What else is missing?\n",
"# Twice the same range\n",
"assert range_overlap([(0, 2), (0, 2)]) == (0.0, 2.0)\n",
"# Two non-overlapping ranges. What is the desired output? (0, 0)? None?\n",
"assert range_overlap([ (0.1, 1.0), (3.0, 4.0) ]) == None\n",
"# More than two non-overlapping ranges. See above.\n",
"assert range_overlap([ (0.1, 1.0), (3.0, 4.0), (10, 12) ]) == None\n",
"# Two touching intervals\n",
"assert range_overlap([(0, 2), (2, 4)]) == None\n",
"# empty input?\n",
"assert range_overlap([]) == None"
]
},
{
"cell_type": "code",
"metadata": {},
"outputs": [],
"source": [
"def range_overlap(ranges):\n",
" if len(ranges) == 0:\n",
" return(None)\n",
" max_left = ranges[0][0]\n",
" min_right = ranges[0][1]\n",
" # ranges[2:] returns an empty list if len(ranges) == 1 and nothing is being looped over.\n",
" for (left, right) in ranges[1:]:\n",
" max_left = max(max_left, left)\n",
" min_right = min(min_right, right)\n",
" if max_left >= min_right:\n",
" return(None)\n",
" return (max_left, min_right)"
]
},
{
"cell_type": "code",
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
"metadata": {},
"outputs": [],
"source": [
"# Single range\n",
"assert range_overlap([ (0.0, 1.0) ]) == (0.0, 1.0)\n",
"# Two overlapping ranges\n",
"assert range_overlap([ (2.0, 3.0), (2.0, 4.0) ]) == (2.0, 3.0)\n",
"# Three overlapping ranges\n",
"assert range_overlap([ (0.0, 1.0), (0.0, 2.0), (-1.0, 1.0) ]) == (0.0, 1.0)\n",
"\n",
"# What else is missing?\n",
"# Twice the same range\n",
"assert range_overlap([(0, 2), (0, 2)]) == (0.0, 2.0)\n",
"# Two non-overlapping ranges. What is the desired output? (0, 0)? None?\n",
"assert range_overlap([ (0.1, 1.0), (3.0, 4.0) ]) == None\n",
"# More than two non-overlapping ranges. See above.\n",
"assert range_overlap([ (0.1, 1.0), (3.0, 4.0), (10, 12) ]) == None\n",
"# Two touching intervals\n",
"assert range_overlap([(0, 2), (2, 4)]) == None\n",
"# empty input?\n",
"assert range_overlap([]) == None"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.4"
}
},
"nbformat": 4,
"nbformat_minor": 2
}