Errors and exceptions
When something goes wrong at runtime, Python raises an exception. Unhandled, it stops the program and prints a traceback. Handled, it lets your program recover, report the problem clearly, or clean up before exiting.
What an error looks like
numbers = [1, 2, 3]
print(numbers[5])Traceback (most recent call last):
File "example.py", line 2, in <module>
print(numbers[5])
IndexError: list index out of rangeRead tracebacks from the bottom up: the last line names the exception and explains it, the lines above show where it happened.
try and except
try:
value = int("abc")
except ValueError:
print("That was not a whole number")
print("The program continues")Only the code inside try is protected. If it raises the named exception, Python jumps to the except block.
Catching several exceptions
def divide(a, b):
try:
return a / b
except ZeroDivisionError:
return "cannot divide by zero"
except TypeError:
return "both values must be numbers"
print(divide(10, 2))
print(divide(10, 0))
print(divide(10, "x"))Or in one clause: except (ZeroDivisionError, TypeError):.
Using the exception object
try:
open("missing.txt")
except FileNotFoundError as e:
print("Problem:", e)
print("Type:", type(e).__name__)else and finally
else runs if no exception was raised. finally runs no matter what, even if there was a return or an unhandled error. It is for cleanup.
def parse(text):
try:
n = int(text)
except ValueError:
print("invalid")
else:
print("parsed", n)
finally:
print("done with", repr(text))
parse("42")
parse("x")Raising exceptions
Use raise when your code detects a problem it cannot fix.
def set_age(age):
if age < 0:
raise ValueError(f"age cannot be negative, got {age}")
return age
try:
set_age(-5)
except ValueError as e:
print(e)Pick the built in exception that best describes the problem: ValueError for a bad value, TypeError for a wrong type, KeyError for a missing key, and so on.
Custom exceptions
Define your own when callers need to catch your specific error.
class InsufficientFunds(Exception):
pass
def withdraw(balance, amount):
if amount > balance:
raise InsufficientFunds(f"need {amount}, have {balance}")
return balance - amount
try:
withdraw(100, 250)
except InsufficientFunds as e:
print("Declined:", e)Common built in exceptions
| Exception | When |
|---|---|
ValueError | Right type, unacceptable value: int("x") |
TypeError | Wrong type: "a" + 1 |
KeyError | Missing dictionary key |
IndexError | List index out of range |
AttributeError | Object has no such attribute or method |
NameError | Variable not defined |
ZeroDivisionError | Division by zero |
FileNotFoundError | File does not exist |
ImportError | Module cannot be imported |
Do not catch everything
except: with no type, or except Exception:, hides bugs. Catch the specific exceptions you expect and know how to handle. Let the rest surface so you can see and fix them.
Practice
- Write a function
safe_int(text)that returns the integer orNoneif the text is not a number. - Write
get_item(lst, i)that returns a friendly message instead of raising on a bad index. - Create a
TooManyStudentsexception and raise it when a class list would exceed 30 names.
