Exceptions
An exception is Python’s way of signaling that something went wrong while a program was running. Instead of returning a special error code that a caller might forget to check, Python raises an object describing the problem and immediately stops normal execution, unwinding the call stack until something handles it — or the program crashes with a traceback. This is the language’s primary error-handling mechanism, and understanding it is essential for writing programs that fail predictably instead of silently producing wrong results.
Exceptions Are Objects
Every exception in Python is an instance of a class. When something goes wrong, Python creates an instance of the appropriate exception class, attaches a message and other data to it, and raises it. If nothing catches it, the interpreter prints a traceback and exits.
# Dividing by zero raises an exception object result = 10 / 0
Traceback (most recent call last):
File "example.py", line 2, in <module>
result = 10 / 0
ZeroDivisionError: division by zeroThe Exception Hierarchy
Exception classes form a tree. At the root is BaseException, which almost nothing catches directly because it also covers things like SystemExit and KeyboardInterrupt — signals that a program is being deliberately stopped, not that something broke. Below that sits Exception, the ancestor of essentially every error you will actually want to catch or raise yourself. Below Exception come progressively more specific classes: LookupError is a parent of both IndexError and KeyError, ArithmeticError is a parent of ZeroDivisionError, and so on.
BaseException
+-- SystemExit
+-- KeyboardInterrupt
+-- Exception
+-- ArithmeticError
| +-- ZeroDivisionError
+-- LookupError
| +-- IndexError
| +-- KeyError
+-- ValueError
+-- TypeError
+-- AttributeError
+-- FileNotFoundError (an OSError subclass)
+-- ... many moreCommon Built-in Exceptions
You will encounter the same handful of exception types constantly. Knowing what typically triggers each one makes debugging much faster.
Exception | Typically raised when... |
|---|---|
ValueError | A function receives an argument of the right type but an inappropriate value, e.g. |
TypeError | An operation or function is applied to an object of the wrong type, e.g. |
KeyError | A dictionary lookup uses a key that does not exist, e.g. |
IndexError | A sequence is indexed with a position outside its valid range, e.g. |
ZeroDivisionError | The second operand of a division or modulo is zero, e.g. |
FileNotFoundError | Code tries to open a file that does not exist on disk, e.g. |
AttributeError | Code accesses an attribute or method that does not exist on an object, e.g. |
Reading a Traceback
A traceback looks intimidating at first, but it is just a record of the call stack at the moment the exception was raised, printed top to bottom in the order calls happened. The crucial habit is to read it bottom-up: the very last line names the exception type and the message, and the lines above it show the chain of function calls that led there, with the deepest call (where the error actually occurred) at the bottom of that chain.
def get_price(catalog, name):
return catalog[name]["price"]
def checkout(catalog, name):
price = get_price(catalog, name)
return price * 1.08
catalog = {"book": {"price": 12}}
checkout(catalog, "pen")Traceback (most recent call last):
File "shop.py", line 9, in <module>
checkout(catalog, "pen")
File "shop.py", line 5, in checkout
price = get_price(catalog, name)
File "shop.py", line 2, in get_price
return catalog[name]["price"]
KeyError: 'pen'Start at the bottom:
KeyError: 'pen'tells you exactly what went wrong and with which value.Move up one frame:
catalog[name]["price"]inget_priceis the exact line that failed.Keep moving up to see why that line ran:
checkoutcalled it, and the top-level script calledcheckout("pen")with a name not in the catalog.The fix belongs wherever the bad input first entered the program — here, validating
namebefore callingcheckout, or handling the missing key insideget_price.