{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "*This notebook contains an excerpt from the [Whirlwind Tour of Python](http://www.oreilly.com/programming/free/a-whirlwind-tour-of-python.csp) by Jake VanderPlas; the content is available [on GitHub](https://github.com/jakevdp/WhirlwindTourOfPython).*\n", "\n", "*The text and code are released under the [CC0](https://github.com/jakevdp/WhirlwindTourOfPython/blob/master/LICENSE) license; see also the companion project, the [Python Data Science Handbook](https://github.com/jakevdp/PythonDataScienceHandbook).*\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "< [Defining and Using Functions](08-Defining-Functions.ipynb) | [Contents](Index.ipynb) | [Iterators](10-Iterators.ipynb) >" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Errors and Exceptions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "No matter your skill as a programmer, you will eventually make a coding mistake.\n", "Such mistakes come in three basic flavors:\n", "\n", "- *Syntax errors:* Errors where the code is not valid Python (generally easy to fix)\n", "- *Runtime errors:* Errors where syntactically valid code fails to execute, perhaps due to invalid user input (sometimes easy to fix)\n", "- *Semantic errors:* Errors in logic: code executes without a problem, but the result is not what you expect (often very difficult to track-down and fix)\n", "\n", "Here we're going to focus on how to deal cleanly with *runtime errors*.\n", "As we'll see, Python handles runtime errors via its *exception handling* framework." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Runtime Errors\n", "\n", "If you've done any coding in Python, you've likely come across runtime errors.\n", "They can happen in a lot of ways.\n", "\n", "For example, if you try to reference an undefined variable:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [ { "ename": "NameError", "evalue": "name 'Q' is not defined", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mNameError\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[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mQ\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mNameError\u001b[0m: name 'Q' is not defined" ] } ], "source": [ "print(Q)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Or if you try an operation that's not defined:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false }, "outputs": [ { "ename": "TypeError", "evalue": "unsupported operand type(s) for +: 'int' and 'str'", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\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[0;36m1\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;34m'abc'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: unsupported operand type(s) for +: 'int' and 'str'" ] } ], "source": [ "1 + 'abc'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Or you might be trying to compute a mathematically ill-defined result:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false }, "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[0;36m2\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mZeroDivisionError\u001b[0m: division by zero" ] } ], "source": [ "2 / 0" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Or maybe you're trying to access a sequence element that doesn't exist:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false }, "outputs": [ { "ename": "IndexError", "evalue": "list index out of range", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mIndexError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0mL\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m3\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mL\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1000\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mIndexError\u001b[0m: list index out of range" ] } ], "source": [ "L = [1, 2, 3]\n", "L[1000]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that in each case, Python is kind enough to not simply indicate that an error happened, but to spit out a *meaningful* exception that includes information about what exactly went wrong, along with the exact line of code where the error happened.\n", "Having access to meaningful errors like this is immensely useful when trying to trace the root of problems in your code." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Catching Exceptions: ``try`` and ``except``\n", "The main tool Python gives you for handling runtime exceptions is the ``try``...``except`` clause.\n", "Its basic structure is this:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "this gets executed first\n" ] } ], "source": [ "try:\n", " print(\"this gets executed first\")\n", "except:\n", " print(\"this gets executed only if there is an error\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that the second block here did not get executed: this is because the first block did not return an error.\n", "Let's put a problematic statement in the ``try`` block and see what happens:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "let's try something:\n", "something bad happened!\n" ] } ], "source": [ "try:\n", " print(\"let's try something:\")\n", " x = 1 / 0 # ZeroDivisionError\n", "except:\n", " print(\"something bad happened!\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here we see that when the error was raised in the ``try`` statement (in this case, a ``ZeroDivisionError``), the error was caught, and the ``except`` statement was executed.\n", "\n", "One way this is often used is to check user input within a function or another piece of code.\n", "For example, we might wish to have a function that catches zero-division and returns some other value, perhaps a suitably large number like $10^{100}$:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def safe_divide(a, b):\n", " try:\n", " return a / b\n", " except:\n", " return 1E100" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "0.5" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "safe_divide(1, 2)" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "1e+100" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "safe_divide(2, 0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There is a subtle problem with this code, though: what happens when another type of exception comes up? For example, this is probably not what we intended:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "1e+100" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "safe_divide (1, '2')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Dividing an integer and a string raises a ``TypeError``, which our over-zealous code caught and assumed was a ``ZeroDivisionError``!\n", "For this reason, it's nearly always a better idea to catch exceptions *explicitly*:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def safe_divide(a, b):\n", " try:\n", " return a / b\n", " except ZeroDivisionError:\n", " return 1E100" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "1e+100" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "safe_divide(1, 0)" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "collapsed": false }, "outputs": [ { "ename": "TypeError", "evalue": "unsupported operand type(s) for /: 'int' and 'str'", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\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[0msafe_divide\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'2'\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;36msafe_divide\u001b[0;34m(a, b)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0msafe_divide\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mb\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0ma\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0mb\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 4\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mZeroDivisionError\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0;36m1E100\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mTypeError\u001b[0m: unsupported operand type(s) for /: 'int' and 'str'" ] } ], "source": [ "safe_divide(1, '2')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We're now catching zero-division errors only, and letting all other errors pass through un-modified." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Raising Exceptions: ``raise``\n", "We've seen how valuable it is to have informative exceptions when using parts of the Python language.\n", "It's equally valuable to make use of informative exceptions within the code you write, so that users of your code (foremost yourself!) can figure out what caused their errors.\n", "\n", "The way you raise your own exceptions is with the ``raise`` statement. For example:" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "collapsed": false }, "outputs": [ { "ename": "RuntimeError", "evalue": "my error message", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mRuntimeError\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[0;32mraise\u001b[0m \u001b[0mRuntimeError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"my error message\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mRuntimeError\u001b[0m: my error message" ] } ], "source": [ "raise RuntimeError(\"my error message\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As an example of where this might be useful, let's return to our ``fibonacci`` function that we defined previously:" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def fibonacci(N):\n", " L = []\n", " a, b = 0, 1\n", " while len(L) < N:\n", " a, b = b, a + b\n", " L.append(a)\n", " return L" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "One potential problem here is that the input value could be negative.\n", "This will not currently cause any error in our function, but we might want to let the user know that a negative ``N`` is not supported.\n", "Errors stemming from invalid parameter values, by convention, lead to a ``ValueError`` being raised:" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def fibonacci(N):\n", " if N < 0:\n", " raise ValueError(\"N must be non-negative\")\n", " L = []\n", " a, b = 0, 1\n", " while len(L) < N:\n", " a, b = b, a + b\n", " L.append(a)\n", " return L" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "fibonacci(10)" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "collapsed": false }, "outputs": [ { "ename": "ValueError", "evalue": "N must be non-negative", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mValueError\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[0mfibonacci\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m10\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;36mfibonacci\u001b[0;34m(N)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mfibonacci\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mN\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mN\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[0;32m----> 3\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"N must be non-negative\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 4\u001b[0m \u001b[0mL\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0ma\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mb\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mValueError\u001b[0m: N must be non-negative" ] } ], "source": [ "fibonacci(-10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now the user knows exactly why the input is invalid, and could even use a ``try``...``except`` block to handle it!" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "trying this...\n", "Bad value: need to do something else\n" ] } ], "source": [ "N = -10\n", "try:\n", " print(\"trying this...\")\n", " print(fibonacci(N))\n", "except ValueError:\n", " print(\"Bad value: need to do something else\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Diving Deeper into Exceptions\n", "\n", "Briefly, I want to mention here some other concepts you might run into.\n", "I'll not go into detail on these concepts and how and why to use them, but instead simply show you the syntax so you can explore more on your own." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Accessing the error message\n", "\n", "Sometimes in a ``try``...``except`` statement, you would like to be able to work with the error message itself.\n", "This can be done with the ``as`` keyword:" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Error class is: \n", "Error message is: division by zero\n" ] } ], "source": [ "try:\n", " x = 1 / 0\n", "except ZeroDivisionError as err:\n", " print(\"Error class is: \", type(err))\n", " print(\"Error message is:\", err)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With this pattern, you can further customize the exception handling of your function." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Defining custom exceptions\n", "In addition to built-in exceptions, it is possible to define custom exceptions through *class inheritance*.\n", "For instance, if you want a special kind of ``ValueError``, you can do this:" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "collapsed": false }, "outputs": [ { "ename": "MySpecialError", "evalue": "here's the message", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mMySpecialError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;32mpass\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mMySpecialError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"here's the message\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mMySpecialError\u001b[0m: here's the message" ] } ], "source": [ "class MySpecialError(ValueError):\n", " pass\n", "\n", "raise MySpecialError(\"here's the message\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This would allow you to use a ``try``...``except`` block that only catches this type of error:" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "do something\n", "do something else\n" ] } ], "source": [ "try:\n", " print(\"do something\")\n", " raise MySpecialError(\"[informative error message here]\")\n", "except MySpecialError:\n", " print(\"do something else\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You might find this useful as you develop more customized code." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## ``try``...``except``...``else``...``finally``\n", "In addition to ``try`` and ``except``, you can use the ``else`` and ``finally`` keywords to further tune your code's handling of exceptions.\n", "The basic structure is this:" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "try something here\n", "this happens only if it succeeds\n", "this happens no matter what\n" ] } ], "source": [ "try:\n", " print(\"try something here\")\n", "except:\n", " print(\"this happens only if it fails\")\n", "else:\n", " print(\"this happens only if it succeeds\")\n", "finally:\n", " print(\"this happens no matter what\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The utility of ``else`` here is clear, but what's the point of ``finally``?\n", "Well, the ``finally`` clause really is executed *no matter what*: I usually see it used to do some sort of cleanup after an operation completes." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "< [Defining and Using Functions](08-Defining-Functions.ipynb) | [Contents](Index.ipynb) | [Iterators](10-Iterators.ipynb) >" ] } ], "metadata": { "anaconda-cloud": {}, "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 }