Catching errors with try and except
A crash is Python telling you something it could not do. Sometimes that is a bug to fix. Sometimes it is a situation you knew was possible — a file that is not there, a number the user typed as words — and the program should handle it and carry on.
typed = "twelve"
try:
age = int(typed)
print(f"Next year you will be {age + 1}")
except ValueError:
print("Please type your age using digits.")
Please type your age using digits.
The try block runs. If it raises the kind of error named in except, the
except block runs instead of the program stopping.
The shape
prices = [250, 120]
try:
print(prices[5])
except IndexError:
print("There is no item at that position.")
There is no item at that position.
Two things worth noticing. The try block stops at the failing line —
anything after it inside try is skipped. And execution continues normally
after the whole try/except, which is the point.
try:
print("first")
print(1 / 0)
print("never runs")
except ZeroDivisionError:
print("caught it")
print("carrying on")
first caught it carrying on
Catch the specific error
The single most important habit here. except on its own catches everything,
including the bugs you wanted to hear about.
prices = [250, 120]
try:
total = sum(prices)
print(f"Totl: {totl}")
except:
print("Something went wrong with the prices.")
Something went wrong with the prices.
There is nothing wrong with the prices. There is a typo — totl — and a bare
except swallowed the NameError and printed a confident, wrong explanation.
Now you have a program that reports a data problem every time it runs, and the
real cause is invisible.
Name the error you are prepared for:
prices = [250, 120]
try:
print(prices[5])
except IndexError:
print("No item at that position.")
No item at that position.
Anything else still crashes, with a traceback pointing at the real line. That is what you want — you are handling one anticipated situation, not muting the program.
The errors worth knowing by name:
| Exception | Raised when |
|---|---|
ValueError |
Right type, wrong value — int("abc") |
TypeError |
Wrong type — "a" + 1 |
KeyError |
Dictionary key not present |
IndexError |
List position out of range |
ZeroDivisionError |
Division by zero |
FileNotFoundError |
The file is not there |
AttributeError |
The object has no such method |
Several except blocks
def divide(text_a, text_b):
try:
return int(text_a) / int(text_b)
except ValueError:
return "Both must be numbers."
except ZeroDivisionError:
return "Cannot divide by zero."
print(divide("10", "2"))
print(divide("ten", "2"))
print(divide("10", "0"))
5.0 Both must be numbers. Cannot divide by zero.
Python tries each except in order and runs the first that matches, so put
specific exceptions before general ones. Exception matches almost everything,
so it must come last if you use it at all.
To handle several the same way, use a tuple:
try:
value = int("abc")
except (ValueError, TypeError):
print("Could not read that as a number.")
Could not read that as a number.
Looking at the exception
as gives you the exception object, which carries the message.
try:
int("twelve")
except ValueError as error:
print(f"Could not convert: {error}")
Could not convert: invalid literal for int() with base 10: 'twelve'
Printing the message is far better than printing your own guess at what happened, especially in a log somebody else will read.
else and finally
else runs when the try block raised nothing. finally runs either way.
try:
value = int("42")
except ValueError:
print("bad input")
else:
print(f"parsed {value}")
finally:
print("done")
parsed 42 done
try:
value = int("abc")
except ValueError:
print("bad input")
else:
print(f"parsed {value}")
finally:
print("done")
bad input done
else is worth using because it keeps the try block down to the one line
that can actually fail. The wider the try, the more likely it catches
something you did not mean it to.
finally runs even if the function returns or the exception is re-raised,
which makes it the place for cleanup — closing a file, releasing a lock.
Most of the time a with block is better still:
with open("orders.txt") as f:
data = f.read()
# closed automatically, error or not
Do not swallow errors silently
try:
value = int("abc")
except ValueError:
pass
print("finished")
finished
That runs cleanly and tells nobody anything. value was never assigned, so the
next line that uses it fails somewhere unrelated with a NameError, and the
real cause is a pass you wrote three files away.
If you genuinely want to ignore something, ignore it visibly:
def to_int(text, default=0):
"""Return text as an int, or default if it is not a number."""
try:
return int(text)
except ValueError:
return default
print(to_int("42"))
print(to_int("abc"))
print(to_int("abc", default=-1))
42 0 -1
The failure now produces a defined value, the caller chooses what it is, and nothing is hidden.
Ask forgiveness, not permission
Python culture prefers trying the thing and handling the failure over checking first. The habit even has an acronym — EAFP, easier to ask forgiveness than permission.
stock = {"chai": 12}
# checking first
if "coffee" in stock:
print(stock["coffee"])
else:
print(0)
# trying it
try:
print(stock["coffee"])
except KeyError:
print(0)
# or just
print(stock.get("coffee", 0))
0 0 0
All three work. The third is best here, because dictionaries already have the
tool. The general point stands though: a try around the real operation is
often clearer than a check that duplicates the operation's own logic — and it
has no gap between the check and the use.
When to catch at all
Catch an exception when you can do something useful about it: substitute a default, ask again, try another source, or report it in terms the user understands.
If all you would do is print "an error occurred" and stop, let it crash. The traceback contains the file, the line and the cause, and that is far more use to whoever has to fix it than a message you invented.
Next: raising and asserting, which is the other side — signalling a problem from inside your own code.
Prefer learning with a teacher?
WasiLearn Academy runs small-group classes covering this material, online and in Karachi.
See the classes