1. Exceptions
Welcome back! Let's talk about exceptions. We'll start with a brief recap, and then learn how to define custom exceptions.
2. Errors are exceptions
Some statements in Python will cause an error when you try to execute them, for example dividing by zero, combining objects of incompatible types, and many others. These errors are called exceptions. Many exceptions have special names, like ZeroDivisionError or TypeError that you see here.
If exceptions not handled correctly, they will stop the execution of your program entirely. This often makes sense -- for example, if I'm trying to use a variable that I never defined, something must have gone very wrong with my script -- but other times it's undesirable. For example, a division by zero error might be caused by missing data, which isn't always a good reason to terminate the program.
3. Exception handling
Instead, you might want to execute special code to handle this case.
To catch an exception and handle it, use the try-except-finally code: wrap the code that you're worried about in a try block, then add an except block, followed by the name of the particular exception you want to handle, and the code that should be executed when the exception is raised. Then if this particular exception does happen, the program will not terminate, but execute the code in the except block instead. You can also have multiple exception blocks.
And finally, you can use the optional "finally" block that will contain the code that runs no matter what. This block is best used for cleaning up like, for example, closing opened files.
4. Raising exceptions
Sometimes, you want to raise exceptions yourself, for example when some conditions aren't satisfied. You can use the raise keyword, optionally followed by a specific error message in parentheses. The user of the code can then decide to handle the error using try/except.
5. Exceptions are classes
In Python, exceptions are actually classes inherited from built-in classes BaseException or Exception. Here's a glimpse into the huge built-in exception class hierarchy. For example, there's a general class of arithmetic errors, of which zero division error is a subclass.
6. Custom exceptions
You can define your own exceptions in Python! Custom exception classes are useful because they can be specific to your application and can provide more granular handling of errors.
To define a custom exception, just define a class that inherits from the built-in Exception class or one of its subclasses.
The class itself can be empty - inheritance alone is enough to ensure that Python will treat this class as an exception class.
For example, let's define a BalanceError class that inherits from Exception.
Then, in our favorite customer class we raise an exception if a negative balance value is passed to the constructor.
7. Exception in constructor
If we attempt to create a customer with a negative account balance, the BalanceError exception is raised.
In the first chapter, we dealt with this situation by merely printing a message, and then creating an object with zero balance.
Handling it with exceptions is better, because in this case, the constructor terminates, and the customer object is not created at all, instead of being implicitly created with account balance set to zero, despite the error.
This sends a clear signal to the user of the Customer class that something went wrong. The user, on their side,
8. Catching custom exceptions
can decide to handle this exception using try-except block if they want, but we, the author of the code, do not make this decision for them.
For example here, the BalanceError is caught in the except block and if that occurs, a customer is instead created with zero balance.
9. Let's practice!
In the exercises, you'll explore creating and handling whole hierarchies of custom exceptions. Have fun!