{ "cells": [ { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "# Lecture 6: Conditionals and Error Handling\n", "\n", "CSCI 1360E: Foundations for Informatics and Analytics" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Overview and Objectives\n", "\n", "In this lecture, we'll go over how to make \"decisions\" over the course of your code depending on the values certain variables take. We'll also introduce exceptions and how to handle them gracefully. By the end of the lecture, you should be able to\n", "\n", " - Build arbitrary conditional hierarchies to test a variety of possible circumstances\n", " - Catch basic errors and present meaningful error messages in lieu of a Python crash" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Part 1: Conditionals\n", "\n", "Up until now (with the exception of that one review question about finding max and min elements in a list from L4), we've been somewhat hobbled in our coding prowess; we've lacked the tools to make different decisions depending on the values our variables take." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "In fact, let's go ahead and look at the problem of finding the maximum value in a list." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": true, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "x = [51, 65, 56, 19, 11, 49, 81, 59, 45, 73]" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "If we want to figure out the maximum value, we'll obviously need a loop to check each element of the list (which we know how to do), and a variable to store the maximum." ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": true, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "max_val = 0\n", "for element in x:\n", " \n", " # ... now what?\n", "\n", " pass" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "We also know we can check relative values, like `max_val < element`. If this evaluates to `True`, we know we've found a number in the list that's bigger than our current candidate for maximum value. But how do we execute code until this condition, and *this condition alone*?" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "**Enter `if / elif / else` statements!** (otherwise known as \"conditionals\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "We can use the keyword `if`, followed by a statement that evaluates to either `True` or `False`, to determine whether or not to execute the code. For a straightforward example:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Working as intended.\n" ] } ], "source": [ "x = 5\n", "if x < 5:\n", " print(\"How did this happen?!\") # Spoiler alert: this won't happen.\n", "\n", "if x == 5:\n", " print(\"Working as intended.\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "In conjunction with `if`, we also have an `else` clause that we can use to execute whenever the `if` statement doesn't:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Correct.\n" ] } ], "source": [ "x = 5\n", "if x < 5:\n", " print(\"How did this happen?!\") # Spoiler alert: this won't happen.\n", "else:\n", " print(\"Correct.\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "This is great! We can finally finish computing the maximum element of a list!" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The maximum element is: 81\n" ] } ], "source": [ "x = [51, 65, 56, 19, 11, 49, 81, 59, 45, 73]\n", "max_val = 0\n", "for element in x:\n", " if max_val < element:\n", " max_val = element\n", "\n", "print(\"The maximum element is: {}\".format(max_val))" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "We can test conditions! But what if we have multifaceted decisions to make? Let's look at a classic example: assigning letter grades from numerical grades." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": true, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "student_grades = {\n", " 'Jen': 82,\n", " 'Shannon': 75,\n", " 'Natasha': 94,\n", " 'Benjamin': 48,\n", "}" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "We know the 90-100 range is an \"A\", 80-89 is a \"B\", and so on. We can't do just a standard \"if / else\", since we have more than two possibilities here." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "The third and final component of conditionals is the `elif` statement (short for \"else if\")." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "`elif` allows us to evaluate as many options as we'd like, all within *the same conditional context* (this is important). So for our grading example, it might look like this:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Benjamin's letter grade: F\n", "Natasha's letter grade: A\n", "Jen's letter grade: B\n", "Shannon's letter grade: C\n" ] } ], "source": [ "for student, grade in student_grades.items():\n", " letter = ''\n", " if grade >= 90:\n", " letter = \"A\"\n", " elif grade >= 80:\n", " letter = \"B\"\n", " elif grade >= 70:\n", " letter = \"C\"\n", " elif grade >= 60:\n", " letter = \"D\"\n", " else:\n", " letter = \"F\"\n", " \n", " print(\"{}'s letter grade: {}\".format(student, letter))\n" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "Ok, that's neat. But there's still one more edge case: what happens if we want to enforce multiple conditions *simultaneously*?" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "To illustrate, let's go back to our example of finding the maximum value in a list, and this time, let's try to find the *second*-largest value in the list. For simplicity, let's say we've already found the largest value." ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": true, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "x = [51, 65, 56, 19, 11, 49, 81, 59, 45, 73]\n", "max_val = 81 # We've already found it!\n", "second_largest = 0" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Here's the rub: we now have *two* constraints to enforce--the second largest value needs to be larger than pretty much everything in the list, but also needs to be *smaller* than the maximum value. Not something we can encode using `if` / `elif` / `else`." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "Instead, we'll use two more keywords integral to conditionals: **`and`** and **`or`**." ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The second-largest element is: 73\n" ] } ], "source": [ "for element in x:\n", " if second_largest < element and element < max_val:\n", " second_largest = element\n", "\n", "print(\"The second-largest element is: {}\".format(second_largest))" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ " - The first condition, **`second_largest < element`**, is the same as before: if our current estimate of the second largest element is smaller than the latest element we're looking at, it's definitely a candidate for second-largest." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ " - The second condition, **`element < max_val`**, is what ensures we don't just pick the largest value again. This enforces the constraint that the current element we're looking at is also *less than* our known maximum value." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ " - The **`and`** keyword glues these two conditions together: it *requires* that they BOTH be True before the code inside the statement is allowed to execute." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "It would be easy to replicate this with \"nested\" conditionals:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": false, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The second-largest element is: 73\n" ] } ], "source": [ "second_largest = 0\n", "for element in x:\n", " if second_largest < element:\n", " if element < max_val:\n", " second_largest = element\n", "\n", "print(\"The second-largest element is: {}\".format(second_largest))" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "...but your code starts getting a little unwieldy with so many indentations." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "You can glue as many comparisons as you want together with `and`; the whole statement will only be `True` if *every single condition* evaluates to True. This is what `and` means: *everything* must be True." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "The other side of this coin is **`or`**. Like `and`, you can use it to glue together multiple constraints. Unlike `and`, the whole statement will evaluate to True *as long as at least ONE condition is True*. This is far less stringent than `and`, where *ALL* conditions had to be True." ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": false, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2 is an even number.\n", "6 is an even number.\n", "10 is an even number.\n" ] } ], "source": [ "numbers = [1, 2, 5, 6, 7, 9, 10]\n", "for num in numbers:\n", " if num == 2 or num == 4 or num == 6 or num == 8 or num == 10:\n", " print(\"{} is an even number.\".format(num))" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "In this contrived example, I've glued together a bunch of constraints. Obviously, these constraints are mutually exclusive; a number can't be equal to both 2 and 4 at the same time, so `num == 2 and num == 4` would never evaluate to True. However, using `or`, only one of them needs to be True for the statement underneath to execute." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "There's a little bit of intuition to it." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ " - \"I want this AND this\" has the implication of both at once." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ " - \"I want this OR this\" sounds more like either one would be adequate." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "One other important tidbit, concerning not only conditionals, but also lists and booleans: the **`not`** keyword." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "An often-important task in data science, when you have a list of things, is querying whether or not some new piece of information you just received is already in your list. You could certainly loop through the list, asking \"is my new_item == list[item i]?\". But, thankfully, there's a better way:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": false, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Aw man, my lucky number isn't here!\n" ] } ], "source": [ "import random\n", "list_of_numbers = [random.randint(1, 100) for i in range(10)] # Generaets 10 random numbers, between 1 and 100.\n", "if 13 not in list_of_numbers:\n", " print(\"Aw man, my lucky number isn't here!\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Notice a couple things here--" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ " - List comprehensions make an appearance! Can you parse it out?\n", " - The `if` statement asks if the number 13 is NOT found in `list_of_numbers`\n", " - When that statement evaluates to `True`--meaning the number is NOT found--it prints the message." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "If you omit the `not` keyword, then the question becomes: \"is this number in the list?\"" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "collapsed": false, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Sweet, found a 1!\n" ] } ], "source": [ "import random\n", "list_of_numbers = [random.randint(1, 2) for i in range(10)] # Generaets 10 random numbers, between 1 and 2. Yep.\n", "if 1 in list_of_numbers:\n", " print(\"Sweet, found a 1!\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "This works for strings as well: `'some_string' in some_list` will return `True` if that string is indeed found in the list." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "**Be careful** with this. Typing issues can hit you full force here: if you ask `if 0 in some_list`, and it's a list of *floats*, then this operation will always evaluate to `False`." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Similarly, if you ask `if \"shannon\" in name_list`, it will look for the precise string `\"shannon\"` and return `False` even if the string `\"Shannon\"` is in the list. With great power, etc etc." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Part 2: Error Handling\n", "\n", "Yes, errors: plaguing us since Windows 95 (but really, since well before then).\n", "\n", "![bsod](http://i1-news.softpedia-static.com/images/news2/reddit-now-has-a-sub-just-for-public-windows-bsods-494506-5.jpg)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "By now, I suspect you've likely seen your fair share of Python crashes." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ " - `NotImplementedError` from the homework assignments" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ " - `TypeError` from trying to multiply an integer by a string" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ " - `KeyError` from attempting to access a dictionary key that didn't exist" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ " - `IndexError` from referencing a list beyond its actual length" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "or any number of other error messages. These are the standard way in which Python (and most other programming languages) handles error messages." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "The error is known as an **Exception**. Some other terminology here includes:" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ " - An exception is *raised* when such an error occurs. This is why you see the code snippet `raise NotImplementedError` in your homeworks. In other languages such as Java, an exception is \"thrown\" instead of \"raised\", but the meanings are equivalent." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ " - When you are writing code that could potentially raise an exception, you can also write code to *catch* the exception and handle it yourself. When an exception is caught, that means it is handled without crashing the program." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "Here's a fairly classic example: **divide by zero!**" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "![dbz](https://themathbehindthemagic.files.wordpress.com/2014/03/divide-by-zero1.jpg)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "Let's say we're designing a simple calculator application that divides two numbers. We'll ask the user for two numbers, divide them, and return the quotient. Seems simple enough, right?" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "collapsed": true, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "def divide(x, y):\n", " return x / y" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "collapsed": false, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "ename": "ZeroDivisionError", "evalue": "division by zero", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mZeroDivisionError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mdivide\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m11\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;32m\u001b[0m in \u001b[0;36mdivide\u001b[0;34m(x, y)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mdivide\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mx\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mZeroDivisionError\u001b[0m: division by zero" ] } ], "source": [ "divide(11, 0)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "D'oh! The user fed us a 0 for the denominator and broke our calculator. Meanie-face." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "So we know there's a possibility of the user entering a 0. This could be malicious or simply by accident. Since it's only one value that could crash our app, we could in principle have an `if` statement that checks if the denominator is 0. That would be simple and perfectly valid." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "But for the sake of this lecture, let's assume we want to try and catch the `ZeroDivisionError` ourselves and handle it gracefully." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "To do this, we use something called a **`try / except`** block, which is very similar in its structure to `if / elif / else` blocks." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "First, put the code that could potentially crash your program inside a `try` statement. Under that, have a `except` statement that defines\n", "\n", " 1. A variable for the error you're catching, and\n", " 2. Any code that dictates how you want to handle the error" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "collapsed": false, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "def divide_safe(x, y):\n", " quotient = 0\n", " try:\n", " quotient = x / y\n", " except ZeroDivisionError:\n", " print(\"You tried to divide by zero. Why would you do that?!\")\n", " return quotient" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Now if our user tries to be snarky again--" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "collapsed": false, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "You tried to divide by zero. Why would you do that?!\n" ] }, { "data": { "text/plain": [ "0" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "divide_safe(11, 0)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "No error, no crash! Just a \"helpful\" error message." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "Like conditionals, you can also create multiple `except` statements to handle multiple different possible exceptions:" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "collapsed": false, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Caught a NameError!\n" ] } ], "source": [ "import random # For generating random exceptions.\n", "num = random.randint(0, 1)\n", "try:\n", " if num == 1:\n", " raise NameError(\"This happens when you use a variable you haven't defined\")\n", " else:\n", " raise ValueError(\"This happens when you try to multiply a string\")\n", "except NameError:\n", " print(\"Caught a NameError!\")\n", "except ValueError:\n", " print(\"Nope, it was actually a ValueError.\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "If you download this notebook or run it with mybinder and re-run the above cell, the exception should flip randomly between the two." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "Also like conditionals, you can handle multiple errors simultaneously. If, like in the previous example, your code can raise multiple exceptions, but you want to handle them all the same way, you can stack them all in one `except` statement:" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "collapsed": false, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Caught...well, some kinda error, not sure which.\n" ] } ], "source": [ "import random # For generating random exceptions.\n", "num = random.randint(0, 1)\n", "try:\n", " if num == 1:\n", " raise NameError(\"This happens when you use a variable you haven't defined\")\n", " else:\n", " raise ValueError(\"This happens when you try to multiply a string\")\n", "except (NameError, ValueError): # MUST have the parentheses!\n", " print(\"Caught...well, some kinda error, not sure which.\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "If you're like me, and you're writing code that you know could raise one of several errors, but are too lazy to look up *specifically* what errors are possible, you can create a \"catch-all\" by just not specifying anything:" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "collapsed": false, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "I caught something!\n" ] } ], "source": [ "import random # For generating random exceptions.\n", "num = random.randint(0, 1)\n", "try:\n", " if num == 1:\n", " raise NameError(\"This happens when you use a variable you haven't defined\")\n", " else:\n", " raise ValueError(\"This happens when you try to multiply a string\")\n", "except:\n", " print(\"I caught something!\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "Finally--and this is really getting into what's known as *control flow* (quite literally: \"controlling the flow\" of your program)--you can tack an `else` statement onto the very end of your exception-handling block to add some final code to the handler." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Why? This is code that is only executed if **NO** exception occurs. Let's go back to our random number example: instead of raising one of two possible exceptions, we'll raise an exception only if we flip a 1." ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "collapsed": false, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "I caught something!\n" ] } ], "source": [ "import random # For generating random exceptions.\n", "num = random.randint(0, 1)\n", "try:\n", " if num == 1:\n", " raise NameError(\"This happens when you use a variable you haven't defined\")\n", "except:\n", " print(\"I caught something!\")\n", "else:\n", " print(\"HOORAY! Lucky coin flip!\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Again, if you run this notebook yourself and execute the above cell multiple times, you should see it flip between \"I caught something!\" and \"HOORAY!\", signifying that the `else` clause only executes if no exceptions are raised." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Review Questions\n", "\n", "Some questions to discuss and consider:\n", "\n", "1: Go back to the `if` / `elif` / `else` example about student grades. Let's assume, instead of `elif` for the different conditions, you used a bunch of `if` statements, e.g. `if grade >= 90`, `if grade >= 80`, `if grade >= 70`, and so on; effectively, you didn't use `elif` at all, but just used `if`. What would the final output be in this case?\n", "\n", "2: We saw that you can add an `else` statement to the end of an exception handling block, which will run code in the event that no exception is raised. Why is this useful? Why not add the code you want to run in the `try` block itself?\n", "\n", "3: With respect to error handling, we discussed `try`, `except`, and `else` statements. There is actually one more: `finally`, which executes *no matter what*, regardless of whether an exception occurs or not. Why would this be useful?" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Course Administrivia\n", "\n", "How was A1?\n", "\n", "How is A2 going?" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Additional Resources\n", "\n", " 1. Matthes, Eric. *Python Crash Course*. 2016. ISBN-13: 978-1593276034\n", " 2. Grus, Joel. *Data Science from Scratch*. 2015. ISBN-13: 978-1491901427" ] } ], "metadata": { "celltoolbar": "Slideshow", "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.5.1" } }, "nbformat": 4, "nbformat_minor": 0 }