In the previous lesson, you saw that you can pass messages to your exceptions to customize what they display when they get raised.
You can customize your exceptions even more by creating your own user-defined exception classes. Any custom exception you write should inherit from the Exception() parent class. This allows you to work with it in the same way you learned to work with built-in exceptions.
Python Custom Exception
The most fundamental custom exception you could define would be an empty exception:
class MyCustomError(Exception):
pass
The only thing that's different from a built-in Exception() object in this custom exception is its name, MyCustomError().
Nevertheless, you could now use MyCustomError() to raise exceptions, and you could handle these exceptions using try except blocks:
class MyCustomError(Exception):
pass
try:
raise MyCustomError
except MyCustomError:
print("Oh, emptiness!")
Custom exceptions that don't do more than define a new name are quite common in Python. You might wonder where the value lies in doing that.
Granularity: Catch Specific Exceptions
Previously, you raised a ValueError in the case that someone added an age value that was below zero:
age = int(input("Age: "))
if age < 0:
raise ValueError("Looks like you're not born yet.")
That's not bad, but ValueError is a built-in exception, and Python will raise it also in other cases, for example, when a user inputs a string instead of a numeric value:
>>> age = int(input("Age: "))
Age: three
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Input In [4], in <cell line: 1>()
----> 1 age = int(input("Age: "))
ValueError: invalid literal for int() with base 10: 'three'
That's a different situation than when a user inputs a value below 0, yet your program raises the same type of exception. It's true that the custom string message allows you to differentiate the two exception cases, but if you wanted to catch both exceptions and do something different if they occurred, then you're out of luck:
try:
age = int(input("Age: "))
except ValueError:
... # Which of the two occurred?
You can avoid this sticky situation if you define a custom error for your custom error condition.
Defining a custom exception, therefore, gives you more granularity. It allows you to proceed in different ways depending on which exact type of error happened in your application.
Descriptiveness: Understand Your Error Quickly
Another value lies in assigning a descriptive name to your custom exception. A name that describes what happened in a concise way. As you might have guessed, MyCustomError is not a great name for a custom exception.
So, let's fix that and write one that actually makes sense!
Previously, you raised a ValueError in the case that someone added an age value that was below zero. With a custom exception, the code could be more descriptive:
class AgeBelowZeroError(Exception): pass
age = int(input("Age: "))
if age < 0:
raise AgeBelowZeroError("Looks like you're not born yet.")
With a small update to your code, you've added a custom exception, AgeBelowZeroError, that you raise if the age value the user input is below zero. The name of this exception is so descriptive that describing it in text makes the whole paragraph sound superfluous. This is exactly how you want your exceptions to be.
Additionally to the added descriptiveness, you can now catch both cases, a ValueError from non-numeric user input and an AgeBelowZeroError. You can then handle them differently:
class AgeBelowZeroError(Exception): pass
try:
age = int(input("Age: "))
if age < 0:
raise AgeBelowZeroError()
except ValueError:
print("Please enter a numeric value.")
except AgeBelowZeroError:
print("Looks like you've not been born yet.")
else:
print(f"Congratulations, you're {age} years old!")
In this code snippet, you explicitly raised AgeBelowZeroError if the user input was a number below zero. Python's int() will implicitly raise a ValueError if the argument is not a numeric value. The try except block catches both of these exceptions and prints different messages depending on which of them occurred.
Finally, if all went well, you print a short congratulatory message:
Age: -1
Looks like you've not been born yet.
Age: twenty
Please enter a numeric value.
Age: 40
Congratulations, you're 40 years old!
Give it a spin on your own computer and confirm that it works as expected. You should see an output similar to the one shown above.
Advantages of a Descriptive Name
There are several advantages to writing your custom exceptions with a descriptive name.
- Variety: You can raise other exceptions, built-in or user-defined, that tackle other issues that could arise in your code, and you'll still be able to quickly distinguish what issue took place.
- Clarity: You'll quickly know what the exception is about when your program raises it.
- Conciseness: You could even remove the additional text and raise
AgeBelowZeroErrorwithout a string argument, and the issue would still be clear. This might make your codebase less crowded without impacting readability. - Reusability: You can raise the exception in multiple places, and you might never have to explain it in more detail---you're just reusing the exception object.
Custom exceptions are a useful feature that doesn't need a lot of extra code and complications. You can feel good about writing a single line of code to define a custom exception class without any other feature than a great descriptive name. That's how you'll work with custom exceptions most of the time.
Of course, custom exceptions are just Python objects like any other, so you can also do more than just that. In the next lesson, you'll tinker with a custom exception object to make it do some more. However, keep in mind that that's more of a fun exercise than something that you'll often do when writing custom exceptions.
Summary: Python Throw Exception (Custom)
- Python allows you to create custom exceptions
- Custom exceptions should inherit from
Exception - Custom exceptions should have a descriptive name
- The Python
raisekeyword is used to raise an exception - Granularity is provided by custom exceptions since you can catch specific exceptions
- Descriptiveness is increased with custom exceptions since you can understand your errors quickly
Advantages of Custom Exceptions
- Variety
- Clarity
- Conciseness
- Reusability