Exception handling
Handling exceptions
So far, what we’ve seen is that when an exception is raised our program is terminated (or not even run to begin with in the case of a SyntaxError
). However, Python allows us to handle exceptions. What this means is that when an exception is raised, a specific block of code can be executed to deal with the problem.
For this we have, minimally, a try/except compound statement. This involves creating two blocks of code: a try
block and an exception handler—an except
block.
The code in the try
block is code where we want to guard against unhandled exceptions. A try
block is followed by an except
block. The except
block specifies the type of exception we wish to handle, and code for handling the exception.
Input validation with try/except
Here’s an example of input validation using try/except. Let’s say we want a positive integer as input. We’ve seen how to validate input in a while
loop.
while True:
= int(input("Please enter a positive integer: "))
n if n > 0:
break
This ensures that if the user enters an integer that’s less than one, that they’ll be prompted again until they supply a positive integer. But what happens if the naughty user enters something that cannot be converted to an integer?
Please enter a positive integer: cheese
Traceback (most recent call last):
File "/.../code.py", line 90, in runcode
exec(code, self.locals)
File "<input>", line 2, in <module>
ValueError: invalid literal for int() with base 10: 'cheese'
Python cannot convert 'cheese'
to an integer and thus a ValueError
is raised.
So now what? We put the code that could result in a ValueError
in a try
block, and then provide an exception handler in an except
block. Here’s how we’d do it.
while True:
try:
= input("Enter a positive integer: ")
user_input = int(user_input)
n if n > 0:
break
except ValueError:
print(f'"{user_input}" cannot be converted to an int!')
print(f'You have entered {n}, a positive integer.')
Let’s run this code, and try a little mischief:
Enter a positive integer: negative
"negative" cannot be converted to an int!
Enter a positive integer: cheese
"cheese" cannot be converted to an int!
Enter a positive integer: -42
Enter a positive integer: 15
You have entered 15, a positive integer.
See? Now mischief (or failure to read instructions) is handled gracefully.
Getting an index with try/except
Earlier, we saw that .index()
will raise a ValueError
exception if the argument passed to the .index()
method is not found in the underlying sequence.
>>> lst = ['apple', 'boat', 'cat', 'drama']
>>> lst.index('egg')
Traceback (most recent call last):
File "/.../code.py", line 90, in runcode
exec(code, self.locals)
File "<input>", line 1, in <module>
ValueError: 'egg' is not in list
We can use exception handling to improve on this.
= ['apple', 'boat', 'cat', 'drama']
lst = input('Enter a string to search for: ')
s try:
= lst.index(s)
i print(f'The index of "{s}" in {lst} is {i}.')
except ValueError:
print(f'"{s}" was not found in {lst}.')
If we were to enter “egg” at the prompt, this code would print:
"egg" was not found in ['apple', 'boat', 'cat', 'drama']
This brings up the age-old question of whether it’s better to check first to see if you can complete an operation without error, or better to try and then handle an exception if it occurs. Sometimes these two approaches are referred to as “look before you leap” (LBYL) and “it’s easier to ask forgiveness than it is to ask for permission” (EAFP). Python favors the latter approach.
Why is this the case? Usually, EAFP makes your code more readable, and there’s no guarantee that the programmer can anticipate and write all the necessary checks to ensure an operation will be successful.
In this example, it’s a bit of a toss up. We could write:
if s in lst:
print(f'The index of "{s}" in {lst} is {lst.index(s)}.')
else:
print(f'"{s}" was not found in {lst}.')
Or we could write (as we did earlier):
try:
print(f'The index of "{s}" in {lst} is {lst.index(s)}.')
except ValueError:
print(f'"{s}" was not found in {lst}.')
Dos and don’ts
Do:
- Keep
try
blocks as small as possible. - Catch and handle specific exceptions.
- Avoid catching and handling
IndexError
,TypeError
,NameError
. When these occur, it’s almost always due to a defect in programming. Catching and handling these exceptions can hide defects that should be corrected. - Use separate
except
blocks to handle different kinds of exceptions.
ٴDz’t:
- Write one handler for different exception types.
- Wrap all your code in one big
try
block. - Use exception handling to hide programming errors.
- Use bare
except:
orexcept Exception:
—these are too general and might catch things you shouldn’t.
Copyright © 2023–2025 Clayton Cafiero
No generative AI was used in producing this material. This was written the old-fashioned way.