Excellent.
DAY 22 is about error handling done right — not just catching errors, but understanding how exceptions work internally, their cost, and how to design clean failure paths.

Most production Python bugs are exception-design bugs, not syntax bugs.


🧠 DAY 22 — Exceptions Internals

(Hierarchy, Cost Model & Pythonic Error Handling)


🔑 CORE IDEA OF DAY 22

Exceptions are a control-flow mechanism with runtime cost.
Use them to represent exceptional states, not normal logic.


1️⃣ What an Exception REALLY Is

An exception is:

  • An object
  • Derived from BaseException
  • Created, raised, propagated, and optionally handled
try:
    x = 1 / 0
except ZeroDivisionError as e:
    print(type(e))

ZeroDivisionError is an object with:

  • Type
  • Message
  • Traceback

2️⃣ Exception Hierarchy (CRITICAL)

Image
Image
Image
Image

Top-level:

BaseException
 ├── SystemExit
 ├── KeyboardInterrupt
 ├── GeneratorExit
 └── Exception
      ├── ArithmeticError
      │    └── ZeroDivisionError
      ├── ValueError
      ├── TypeError
      ├── KeyError
      └── ...

Golden rule:

Catch Exception, not BaseException

Catching BaseException hides:

  • Ctrl+C
  • System exits
  • Generator shutdown

3️⃣ Raising Exceptions (Explicit Control)

raise ValueError("invalid input")

Or re-raise:

try:
    risky()
except ValueError:
    raise

This preserves the original traceback.


4️⃣ How Exceptions Propagate (Stack Unwinding)

When an exception occurs:

  1. Current frame stops
  2. Stack unwinds frame by frame
  3. Python looks for a matching except
  4. If none found → program crashes

This is why tracebacks show call stacks.


5️⃣ try / except / else / finally (FULL MODEL)

try:
    x = int("10")
except ValueError:
    print("bad input")
else:
    print("success")
finally:
    print("cleanup")

Rules:

  • except → only on exception
  • else → only if no exception
  • finally → always

🧠 Use else to avoid catching errors unintentionally.


6️⃣ Exception Objects Carry Tracebacks

import traceback

try:
    1 / 0
except Exception as e:
    traceback.print_exc()

Tracebacks are:

  • Linked to frames
  • Useful for debugging
  • Expensive to create

7️⃣ Why Exceptions Are SLOW (Important for Design)

Raising an exception:

  • Allocates exception object
  • Captures traceback
  • Unwinds stack

Compared to:

if x == 0:
    ...

👉 Exceptions are orders of magnitude slower than conditionals

Rule:

Don’t use exceptions for normal control flow.


8️⃣ EAFP vs LBYL (Python Philosophy)

LBYL — Look Before You Leap

if key in d:
    value = d[key]

EAFP — Easier to Ask Forgiveness

try:
    value = d[key]
except KeyError:
    ...

Python often prefers EAFP, because:

  • Cleaner code
  • Avoids race conditions
  • Reads naturally

But:

  • Use EAFP where failure is rare
  • Use LBYL in hot loops

9️⃣ Writing Custom Exceptions (BEST PRACTICE)

class ConfigError(Exception):
    pass

Why subclass Exception?

  • Integrates with ecosystem
  • Can be selectively caught
  • Semantic clarity

Avoid:

class MyError(BaseException):  # ❌
    pass

🔟 finally vs Context Managers

Bad:

f = open("file")
try:
    data = f.read()
finally:
    f.close()

Better:

with open("file") as f:
    data = f.read()

Context managers:

  • Cleaner
  • Safer
  • Exception-aware by design

11️⃣ Exception Chaining (Advanced, Interview Gold)

try:
    int("x")
except ValueError as e:
    raise RuntimeError("conversion failed") from e

Traceback shows:

The above exception was the direct cause of...

This preserves root cause.


12️⃣ Silent Exception Anti-Pattern 🚨

try:
    do_something()
except Exception:
    pass

This:

  • Hides bugs
  • Breaks debugging
  • Fails silently

Always:

  • Log
  • Re-raise
  • Or handle explicitly

🔥 INTERVIEW TRAPS (DAY 22)

Q1

Why shouldn’t we catch BaseException?

✔ Swallows KeyboardInterrupt
✔ Prevents graceful exit


Q2

Which is faster?

if x in d:

or

try: d[x]

✔ Depends on failure frequency
if faster when misses common


Q3

Why context managers are preferred over try/finally?

✔ Less error-prone
✔ Cleaner semantics
✔ Automatic cleanup


🧠 DAY 22 MENTAL CHECKLIST

When handling errors:

  1. Is this exceptional or normal?
  2. Should I use EAFP or LBYL?
  3. Am I catching too broadly?
  4. Is cleanup guaranteed?
  5. Is error information preserved?

📝 DAY 22 ASSIGNMENT (MANDATORY)

1️⃣ Predict output (NO RUNNING):

try:
    raise ValueError("x")
except Exception:
    print("A")
finally:
    print("B")

2️⃣ Explain clearly:

  • Why exceptions are slow
  • Difference between except and else

3️⃣ Design question:

How would you design error handling for a data pipeline step that can fail due to bad input, network issues, or system shutdown?

(Hint: custom exceptions + chaining)


🔜 DAY 23 PREVIEW

DAY 23 — Iterators & Generators (Lazy Evaluation & State Machines)

You’ll learn:

  • Iterator protocol deeply
  • How generators pause/resume execution
  • Memory benefits
  • Why generators power Python’s scalability

When ready, say 👉 “START DAY 23”