Let's talk about how to throw 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.
Wow 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.
Intro to Python courses often skip over some fundamental Python concepts.
Sign up below and I'll explain concepts that new Python programmers often overlook.
Intro to Python courses often skip over some fundamental Python concepts.
Sign up below and I'll share ideas new Pythonistas often overlook.