Skip to main content

08.1 - Exceptions & Try/Except/Finally

Theory 25 min Intermediate

What is an Exception?

An exception is an error that occurs during execution. If not handled, it crashes the program.

# This crashes with ZeroDivisionError
result = 10 / 0

# This crashes with ValueError
int("hello")

# This crashes with FileNotFoundError
open("missing.txt")

Exception Hierarchy

BaseException
├── SystemExit
├── KeyboardInterrupt
├── GeneratorExit
└── Exception
├── ArithmeticError
│ ├── ZeroDivisionError
│ └── OverflowError
├── AttributeError
├── FileNotFoundError (IOError)
├── ImportError
├── IndexError
├── KeyError
├── MemoryError
├── NameError
├── RuntimeError
├── StopIteration
├── TypeError
├── ValueError
└── OSError

try / except / else / finally

try:
# Code that might raise an exception
result = int(input("Enter a number: "))
answer = 10 / result

except ValueError as e:
# Handles ValueError (e.g., non-numeric input)
print(f"Invalid number: {e}")

except ZeroDivisionError:
# Handles division by zero
print("Cannot divide by zero!")

except (TypeError, OverflowError) as e:
# Handle multiple exception types together
print(f"Type/overflow error: {e}")

except Exception as e:
# Catch-all for any other exception
print(f"Unexpected error: {type(e).__name__}: {e}")

else:
# Runs ONLY if NO exception was raised
print(f"Result: {answer}")

finally:
# ALWAYS runs, even if exception occurred
print("Done — cleaning up")

Raising Exceptions

def divide(a, b):
if not isinstance(a, (int, float)):
raise TypeError(f"Expected number, got {type(a).__name__}")
if b == 0:
raise ZeroDivisionError("Cannot divide by zero")
return a / b

# Re-raise with context
def process_data(data):
try:
result = parse(data)
except ParseError as e:
raise ValueError(f"Invalid data format: {data}") from e
# The original exception is chained as __cause__

# Raise without context (suppress original)
raise ValueError("Invalid data") from None

Common Built-in Exceptions

ExceptionWhen
ValueErrorWrong value type or range
TypeErrorWrong type
KeyErrorDict key not found
IndexErrorList index out of range
AttributeErrorObject has no such attribute
FileNotFoundErrorFile doesn't exist
PermissionErrorNo permission to access file
ZeroDivisionErrorDivided by zero
ImportErrorModule not found
RuntimeErrorGeneric runtime error
NotImplementedErrorAbstract method not implemented
StopIterationIterator exhausted

LBYL vs EAFP

StyleDescriptionPython preferred
LBYLLook Before You Leap — check before actingC/Java style
EAFPEasier to Ask Forgiveness than PermissionPython style
# LBYL — check first
if "key" in d:
value = d["key"]

# EAFP — try and handle
try:
value = d["key"]
except KeyError:
value = default

Python strongly favors EAFP — it's more readable and avoids race conditions.


Key Vocabulary

TermDefinition
ExceptionAn error that occurs during program execution
tryBlock where exceptions might occur
exceptBlock that handles a specific exception
elseBlock that runs only if no exception occurred
finallyBlock that always runs, for cleanup
raiseExplicitly raise an exception
Exception chainingraise X from Y — links exceptions
EAFPPythonic style: try first, handle exceptions

Summary

  • Use try/except to handle exceptions gracefully
  • else runs only on success; finally always runs
  • Catch specific exception types — avoid bare except: (catches everything including KeyboardInterrupt)
  • raise ExceptionType("message") raises exceptions with context
  • raise X from Y chains exceptions preserving the original cause
  • Python style is EAFP: try the operation, handle exceptions — don't check first