1.3.7Python Intermediate

Exception hierarchy

2,482 words11 min readdifficulty · medium1 backlinks

The Structure: From General to Specific

Figure — Exception hierarchy

Every exception in Python inherits from BaseException. Here's the derived-from-scratch logic:

WHY does Python need BaseException at the top?

  • To have ONE common ancestor for all exceptions
  • So except Exception: can catch "normal" errors without catching system-exit events

The Main Branches:

BaseException
├── SystemExit          # sys.exit() calls
├── KeyboardInterrupt   # Ctrl+C
├── GeneratorExit       # Generator cleanup
└── Exception           # ← Normal errors start here
    ├── StopIteration
    ├── ArithmeticError
    │   ├── ZeroDivisionError
    │   ├── OverflowError
    │   └── FloatingPointError
    ├── LookupError
    │   ├── IndexError
    │   └── KeyError
    ├── OSError
    │   ├── FileNotFoundError
    │   ├── PermissionError
    │   └── ConnectionError
    └── ... many more

Derivation: How Catching Works

FROM FIRST PRINCIPLES: When Python sees a raise, it walks up the exception hierarchy looking for a matching except clause.

Step-by-step:

  1. Exception raised: KeyError('missing_key')
  2. Python checks: Is there an except KeyError? ✓ Match!
  3. If not found, check parent: Is there an except LookupError? ✓ Match!
  4. If not found, check grandparent: Is there an except Exception? ✓ Match!
  5. Continue until BaseException or no handler found (crash)

WHY this ordering matters:

# ❌ WRONG - Dead code below!
try:
    data[key]
except Exception as e:      # Catches EVERYTHING first
    print("General error")
except KeyError as e:        # NEVER REACHED - KeyError is a child of Exception
    print("Key missing")

WHY? Python checks handlers top to bottom. KeyError IS-A Exception, so the first handler always wins.

# ✅ CORRECT - Specific to general
try:
    data[key]
except KeyError as e:        # Checked first - catches KeyError
    print(f"Missing key: {e}")
except LookupError as e:     # Catches IndexError (sibling of KeyError)
    print(f"Lookup failed: {e}")
except Exception as e:       # Catches everything else
    print(f"Unexpected: {e}")

Worked Examples

Common Mistakes & Steel-manning

Recall Feynman Explanation (Explain to a 12-year-old)

Imagine your teacher asks you to organize all animals. You might say:

  • LivingThings (top of tree)
    • Animals
      • Mammals
        • Dogs, Cats, Whales
      • Birds
        • Parrots, Eagles

Python does the same with errors! Instead of "Animals," we have "Exceptions." Instead of "Mammals," we have "ArithmeticError" (math problems) or "LookupError" (can't find something).

When you write except Mammals, you catch all dogs and cats automatically. When you write except Dogs, you ONLY catch dogs.

The rule: If you want to catch a dog, you can say "catch dogs" OR "catch mammals" OR "catch animals"—they all work because dogs belong to all three families. But if you only care about dogs, be specific! Say "catch dogs" so you don't accidentally catch cats.

In Python: except KeyError (specific) vs except LookupError (catches KeyError + IndexError) vs except Exception (catches almost everything).

Connections

  • 1.3.06-TryExcept-Finally — Basic exception syntax (use hierarchy for precise catching)
  • 1.3.08-Custom-Exceptions — Create your own exception classes in the hierarchy
  • 1.3.09-Context-Managers — Use with statements (which rely on exception handling internally)
  • 2.1.04-Inheritance — Exception hierarchy uses class inheritance (IS-A relationships)
  • 3.2.05-Logging — Log exceptions with proper specificity (don't swallow with bare except)

#flashcards/coding

What is the root of Python's exception hierarchy?
BaseException (parent of SystemExit, KeyboardInterrupt, and Exception)
Why should you use except Exception instead of except BaseException for normal error handling?
BaseException catches SystemExit and KeyboardInterrupt, which would prevent program termination and Ctrl+C from working. Exception catches only normal errors.
What is the parent exception of both KeyError and IndexError?
LookupError (both are children of LookupError)
Why must specific exception handlers come before general ones?
Python checks handlers sequentially top-to-bottom. If a general handler (like except Exception) comes first, it catches child exceptions before specific handlers can run, making them unreachable dead code.
Name three children of ArithmeticError
ZeroDivisionError, OverflowError, FloatingPointError
What happens if you raise KeyError and only have except LookupError in your handler?
The LookupError handler catches it, because KeyError IS-A LookupError (inheritance)
When would you use except OSError instead of specific handlers?
As a safety net after specific handlers (FileNotFoundError, PermissionError) to catch unexpected OS-level errors like disk full or network failures
What's the danger of except Exception: pass in production code?
It silently swallows all errors including bugs, making debugging impossible. You don't know when things fail.
Does 10**1000 / 10**999 raise OverflowError?
No—it equals 10.0. Integer division promotes correctly. OverflowError only occurs with FLOAT operands whose result exceeds the max float (~1.8e308), e.g. 1e308 / 1e-10.

Concept Map

parent of

parent of

parent of

child of

child of

child of

specializes to

specializes to

specializes to

specializes to

caught by handler for

prevents

BaseException

SystemExit

KeyboardInterrupt

Exception

ArithmeticError

LookupError

OSError

ZeroDivisionError

IndexError

KeyError

FileNotFoundError

Specific before General

Dead Code

Hinglish (regional understanding)

Intuition Hinglish mein samjho

Hinglish (regional understanding)

Intuition Hinglish mein samjho

Dekho yaar, Python ne saare exceptions ko ek family tree ki tarah organize kiya hai, jaise ek khandaan hota hai jahan sabka ek common daada hota hai. Yahan sabse upar BaseException baitha hai, uske neeche Exception aata hai, aur uske neeche chote-chote specific errors jaise KeyError, IndexError, ZeroDivisionError waghera. Core intuition ye hai ki jab tum kisi parent exception ko catch karte ho, toh uske saare children automatically catch ho jaate hain. Jaise agar tum "mammals" pakadne jaao, toh dogs bhi aa jaayenge kyunki dog ek mammal hai. Isi tarah except LookupError likhne se KeyError aur IndexError dono pakde jaate hain, kyunki ye dono LookupError ke bacche hain.

Ab ye baat kyun important hai? Kyunki Python except clauses ko top se bottom check karta hai, aur jo pehla match ho jaaye wahi jeet jaata hai. Isliye tumhe hamesha specific error pehle aur general error baad mein likhna hota hai. Agar tumne galti se except Exception sabse upar likh diya, toh neeche ka except KeyError kabhi chalega hi nahi—wo dead code ban jaayega, kyunki KeyError bhi toh ek Exception hi hai, aur pehla handler use pehle hi pakad lega. Ye ek bahut common mistake hai jo beginners karte hain.

Toh practical baat ye hai ki jab bhi error handle karo, sabse specific ancestor use karo jo tumhare kaam ke exceptions cover karta ho—na zyada broad, na zyada narrow. except BaseException toh bilkul avoid karo kyunki wo Ctrl+C (KeyboardInterrupt) aur system-exit tak pakad leta hai, jo tumhare program ko kabhi band hi nahi hone dega—bahut dangerous hai. Is hierarchy ko samajhne se tum "surgical precision" ke saath errors handle kar paoge, matlab exactly wahi pakadoge jo tumhe chahiye, aur baaki errors ko naturally aage jaane doge. Ye skill real projects mein bahut kaam aati hai jab tumhe clean aur robust code likhna ho.

Go deeper — visual, from zero

Test yourself — Python Intermediate

Connections