{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import numpy as np" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Exceptions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "An exception is an event, which occurs during the execution of a program, that disrupts the normal flow of the program's instructions.\n", "\n", "You've already seen some exceptions in the **Debugging** lesson." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Many programs want to know about exceptions when they occur. For example, if the input to a program is a file path. If the user inputs an invalid or non-existent path, the program generates an exception. It may be desired to provide a response to the user in this case." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It may also be that programs will *generate* exceptions. This is a way of indicating that there is an error in the inputs provided. In general, this is the preferred style for dealing with invalid inputs or states inside a python function rather than having an error return." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Catching Exceptions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Python provides a way to detect when an exception occurs. This is done by the use of a block of code surrounded by a \"try\" and \"except\" statement." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def divide(numerator, denominator):\n", " result = numerator/denominator\n", " print(\"result = %f\" % result)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "divide(1.0, 0)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def divide1(numerator, denominator):\n", " try:\n", " result = numerator/denominator\n", " print(\"result = %f\" % result)\n", " except:\n", " print(\"You can't divide by 0!\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "divide1(1.0, 0)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "divide1(1.0, 'a')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "While this does catch the exception, the error message doesn't really match the condition because conceptually `'a' != 0`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "divide1(\"x\", 2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Moreover, this is also not correct, because `2 != 0` but the message says it does because the real error is in the first argument!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def divide2(numerator, denominator):\n", " try:\n", " result = numerator / denominator\n", " print(\"result = %f\" % result)\n", " except (ZeroDivisionError, TypeError) as err:\n", " print(\"Got an exception: %s\" % err)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "divide2(1, \"X\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "divide2(\"x\", 2)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "divide2(1, 0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### What do you do when you get an exception?\n", "\n", "First, you can feel relieved that you caught a problematic element of your software! Yes, relieved. Silent fails are much worse. (Again, another plug for testing.)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Generating Exceptions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Why *generate* exceptions? (Don't I have enough unintentional errors?)" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import pandas as pd\n", "def validateDF(df):\n", " if not \"hours\" in df.columns:\n", " raise ValueError(\"DataFrame should have a column named 'hours'.\")\n", " else:\n", " pass" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df = pd.DataFrame({'hours': range(10) })\n", "validateDF(df)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df = pd.DataFrame({'years': range(10) })\n", "validateDF(df)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Class exercise\n", "Choose one of the functions from the last exercise. Create two new functions:\n", "- The first function throws an exception if there is a negative argument.\n", "- The second function catches an exception if the modulo operator (`%`) throws an exception and attempts to correct it by coercing the argument to a positive integer." ] } ], "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.9.1" } }, "nbformat": 4, "nbformat_minor": 2 }