Let's talk about how to raise an exception in Python.
Here we have a program called is_prime:
from math import sqrt
def is_prime(number):
for candidate in range(2, int(sqrt(number))+1):
if number % candidate == 0:
return False
return True
When we pass a negative number to the is_prime function, it will pass that negative number into the math.sqrt function, which raises a ValueError exception:
>>> is_prime(-2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/trey/is_prime.py", line 5, in is_prime
for candidate in range(2, int(sqrt(number))+1):
ValueError: math domain error
The sqrt function in Python's math module raises an exception when it's given a negative number.
Note that I said "raises" an exception.
In Python we usually talk about "how to raise an exception" instead of "how to throw an exception".
This is partly due to the fact that raise is a Python keyword (as we'll see shortly) while throw is not.
There are probably some cases where is_prime should be raising an exception but isn't right now.
For example, if we pass a floating point number to the is_prime function, it returns an answer:
>>> is_prime(4.0)
False
And sometimes it returns a really weird answer:
>>> is_prime(4.5)
True
It doesn't make sense to ask a non-integer whether it's prime, so our is_prime function should probably raise an exception when given a floating point number.
Also, if we pass in 0 or 1 to is_prime, it returns True:
>>> is_prime(1)
True
It should probably either return False or raise an exception instead (depending on how we'd prefer to implement is_prime).
We could modify our is_prime function to check for these two conditions, raising an exception if either of those conditions are met.
First we'll ask whether the given number is an instance of the float class, and we'll raise a TypeError exception if it is:
if isinstance(number, float):
raise TypeError(f"Only integers are accepted: {number}")
We're using Python's raise statement and passing in a TypeError exception object.
We're using TypeError because the wrong type was given.
Also, if the number given is less than 2, we'll say that this isn't a valid value, so we'll raise a ValueError exception.
The message for our ValueError exception will declare that only integers above 1 are accepted:
if number < 2:
raise ValueError(f"Only integers above 1 are accepted: {number}")
Here's a updated is_prime function with both of those conditions and raise statements:
def is_prime(number):
if isinstance(number, float):
raise TypeError(f"Only integers are accepted: {number}")
if number < 2:
raise ValueError(f"Only integers above 1 are accepted: {number}")
for candidate in range(2, int(sqrt(number))+1):
if number % candidate == 0:
return False
return True
In both of these cases, the error message we give to our exception object shows the number we were given, which will make our error message as helpful as possible when we're debugging our code.
Now when we call the is_prime function with a floating point number, it raises a TypeError exception which says only integers are accepted:
>>> is_prime(4.5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/trey/is_prime2.py", line 6, in is_prime
raise TypeError(f"Only integers are accepted: {number}")
TypeError: Only integers are accepted: 4.5
Similarly, if we call is_prime with 1 or 0, it raises a ValueError exception:
>>> is_prime(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/trey/is_prime2.py", line 8, in is_prime
raise ValueError(f"Only integers above 1 are accepted: {number}")
ValueError: Only integers above 1 are accepted: 1
In both cases, the traceback that Python prints out shows the friendly error message we gave our exception objects.
Where did TypeError and ValueError come from?
We didn't define those classes, so they must be defined in Python.
If you look at help on the builtins module in Python, or if you look at the documentation for exceptions, you'll see the exception hierarchy:
>>> import builtins
>>> help(builtins)
Help on built-in module builtins:
NAME
builtins - Built-in functions, exceptions, and other objects.
DESCRIPTION
Noteworthy: None is the `nil' object; Ellipsis represents `...' in slices.
CLASSES
object
BaseException
Exception
ArithmeticError
FloatingPointError
OverflowError
ZeroDivisionError
AssertionError
AttributeError
BufferError
EOFError
ImportError
ModuleNotFoundError
LookupError
IndexError
KeyError
MemoryError
NameError
UnboundLocalError
OSError
BlockingIOError
ChildProcessError
ConnectionError
BrokenPipeError
ConnectionAbortedError
ConnectionRefusedError
ConnectionResetError
FileExistsError
FileNotFoundError
InterruptedError
IsADirectoryError
NotADirectoryError
PermissionError
ProcessLookupError
TimeoutError
ReferenceError
RuntimeError
NotImplementedError
RecursionError
StopAsyncIteration
StopIteration
SyntaxError
IndentationError
TabError
SystemError
TypeError
ValueError
UnicodeError
UnicodeDecodeError
UnicodeEncodeError
UnicodeTranslateError
Warning
BytesWarning
DeprecationWarning
EncodingWarning
FutureWarning
ImportWarning
PendingDeprecationWarning
ResourceWarning
RuntimeWarning
SyntaxWarning
UnicodeWarning
UserWarning
GeneratorExit
KeyboardInterrupt
SystemExit
TypeError and ValueError are just two of the many built-in exceptions in Python.
There are dozens of exceptions built into Python.
We don't have to import anything in order to use these exceptions; they're just built-in.
We can define our own custom exception types by inheriting from another exception class, but it's a little bit unusual to do so. Unless you really need to distinguish your exceptions from exceptions that are built into Python, you probably won't create custom exceptions often.
The most common exception type I raise in my code is a ValueError exception.
raise to throw an exception in PythonIf you have a specific condition in your function that should loudly crash your program (if/when that condition is met) you can raise an exception (a.k.a. "throw an exception") by using the raise statement and providing an exception object to raise.
You can make an exception object by calling an exception class, passing in a helpful error message.
Python Jumpstart is designed to help new Python programmers get up to speed quickly. Get hands-on practice with 50 bite-sized modules that build your Python skills step by step.
Sign up for my 5 day email course and learn essential concepts that introductory courses often overlook!
Sign in to your Python Morsels account to track your progress.
Don't have an account yet? Sign up here.
Sign up for my free 5 day email course and learn essential concepts that introductory courses often overlook: iterables, callables, pointers, duck typing, and namespaces. Learn to avoid beginner pitfalls, in less than a week!
Ready to level up? Sign up now to begin your Python journey the right way!