1. Error handling
Now you're familiar with errors, let's look at how we can handle them in our custom functions.
2. Pandas traceback
Remember this traceback from trying to subset a nonexistent column in a pandas DataFrame?
In lines 3804 and 3805 of base.py we see the terms except and raise. These are keywords used to perform an action if an error occurs, specifically a KeyError.
Part of being a good developer is anticipating where others might incorrectly use our code and providing them with information on how to resolve the issue.
3. Design-thinking
We should consider how developers will want to use our code, just like city planners should consider how people want to travel between different locations.
Therefore, we should think about how people might use our custom functions, try running them in these different ways, and see what outputs or errors are produced.
4. Error handling in custom functions
Let's look at how we can help people who might use this custom average function.
5. Where might they go wrong?
The function has been written with the expectation that the single argument is a data structure such as a list or set.
This might not be obvious, resulting in developers seeking the average of several values, each passed as positional arguments. Alternatively, they could provide a dictionary, expecting our function to infer the data type and complete the calculation accordingly.
6. Where might they go wrong?
Let's focus on the second example.
As expected, providing a dictionary results in a TypeError.
7. Error-handling techniques
Previously, we've looked at ways to avoid this problem, including control flow to check data types and writing docstrings that state the data type accepted.
We can also use a technique within the body of the function.
This involves some new keywords.
8. try-except
We can use a try-except block. To do this, we use the try keyword followed by a colon. We then indent the following line and write the code we want to try to execute, which is the function body.
Afterward, we use the except keyword, which instructs Python on what code to run if the code inside the try block results in an error. Again, we indent the next line, and we can provide a descriptive print message highlighting the potential issue with how the function has been used.
9. raise
Alternatively, we can choose to raise an error and customize the message we provide.
Here, we check if an appropriate data type was used. If so, we run the function.
10. raise
Otherwise, we use the raise keyword, which instructs Python to raise an Exception.
11. raise TypeError
We follow this with the specific type of error, calling it so that we can provide a custom output message!
12. raise TypeError output
Calling the average function on a dictionary now returns a TypeError, as it previously would have, but it now contains our custom message. This makes debugging much easier!
13. try-except vs. raise
Each approach has benefits and use cases.
Using try-except, if an error occurs, the code in the error block will run, and the program will not terminate! This can be useful if our code is not sequential, meaning if one section fails, it won't impact the remainder of the script.
Conversely, the raise keyword is designed to produce an error. This is useful if we want to avoid running subsequent code that may cause an unintended result.
14. Let's practice!
It's time for you to try error handling!