You think errors stop your program. In Python, they are part of control flow.

Today, you understand how Python handles errors under the hood.


Today’s Goal

By the end of today, you will:

  • Understand how exceptions work internally
  • Learn stack unwinding
  • Use exceptions correctly (and not overuse them)
  • Understand performance implications

The Illusion

x = int("abc")

You think:

Program crashes

Reality:

Python raises an exception object and unwinds the stack


What Is an Exception?

An exception is:

  • an object
  • representing an error condition

Basic Example

try:
    x = int("abc")
except ValueError:
    print("invalid input")

What Happens Internally

  1. Error occurs
  2. Exception object created
  3. Current frame interrupted
  4. Stack unwinds
  5. Matching except block executed

Stack Unwinding

[ function3 ]
[ function2 ]
[ function1 ]
[ main ]

Exception moves up until handled.


Key Insight

Exceptions are propagated up the call stack.


Multiple Except Blocks

try:
    x = int("abc")
except ValueError:
    print("value error")
except Exception:
    print("general error")

Order Matters

Specific exceptions should come before general ones.


Finally Block

try:
    f = open("file.txt")
finally:
    print("cleanup")

Always executes.


Else Block

try:
    x = int("10")
except:
    pass
else:
    print("success")

Runs only if no exception occurs.


Raising Exceptions

raise ValueError("invalid value")

Custom Exceptions

class MyError(Exception):
    pass

Exception Cost

Exceptions are expensive.

Because:

  • object creation
  • stack unwinding
  • frame cleanup

Performance Insight

Do NOT use exceptions for normal control flow.

Bad:

try:
    x = d["key"]
except KeyError:
    x = None

Better:

x = d.get("key")

Exception Chaining

try:
    raise ValueError()
except ValueError as e:
    raise RuntimeError() from e

Preserves original error.


Tracebacks (Debugging Insight)

When an exception is not handled, Python prints a traceback:

Traceback (most recent call last):
  File "main.py", line 2, in <module>
    x = int("abc")
ValueError: invalid literal for int()

What It Shows

  • call stack (from bottom to top)
  • exact line where error occurred
  • exception type and message

Using traceback Module

import traceback

try:
    int("abc")
except:
    traceback.print_exc()

Insight

Traceback is your window into stack state during failure.


Why This Matters

Exceptions affect:

  • control flow
  • debugging
  • performance

Real Example

def divide(a, b):
    if b == 0:
        raise ValueError("division by zero")
    return a / b

Your Task

  • create custom exception
  • observe stack trace
  • compare try/except vs condition checks

Common Mistakes

  • catching broad Exception
  • swallowing errors silently
  • using exceptions as logic

Think Deeper

  1. What happens to stack during exception?
  2. Why are exceptions expensive?
  3. When should you raise vs return?

Subtle Insight (CRITICAL)

Exceptions are structured control flow, not just errors.


Tomorrow

Iterators & Generators — lazy execution and memory efficiency


Rule

  • Handle specific exceptions
  • Avoid silent failures
  • Use exceptions wisely

See you in Day 8.