1.3.6 · D5Python Intermediate
Question bank — Raising exceptions — raise, custom exception classes
Every prompt uses the reveal format: read left, think, then check right.
True or false — justify
True or false: return ValueError("bad") and raise ValueError("bad") both stop the function and signal an error.
False.
return hands back a normal object and the function ends successfully; only raise interrupts flow and propagates. The caller of the return version just gets an exception object and happily proceeds.True or false: a custom exception class must define __init__ to be usable.
False.
class MyError(Exception): pass is fully usable — the name is the value. You only add __init__ when you want to carry structured data (like balance, shortfall).True or false: raise ValueError (no parentheses) is a syntax error because you didn't create an instance.
False. Passing the class is legal; Python instantiates it for you with no arguments.
str(e) is then just empty because .args is empty.True or false: catching except Exception: will catch a KeyboardInterrupt (Ctrl-C).
False.
KeyboardInterrupt inherits from BaseException, not from Exception, so except Exception: deliberately lets it through — that's why you inherit your errors from Exception.True or false: a bare raise inside an except creates a brand-new exception object.
False. It re-raises the same exception currently being handled, keeping its original traceback intact — no new object, no lost history.
True or false: raise B from A hides the original exception A.
False. It does the opposite — it chains them, setting
B.__cause__ = A so the traceback explicitly shows "the above exception was the direct cause."True or false: if no except anywhere catches a raised exception, Python silently ignores it.
False. It propagates all the way to the top of the call stack, prints the full traceback, and exits the program with a non-zero status.
True or false: you can raise any object you like, such as a string or an integer.
False. The thing after
raise must be an exception instance or an exception class (subclass of BaseException); anything else gives TypeError: exceptions must derive from BaseException.Spot the error
What's wrong with class MyError(BaseException): pass?
It inherits from
BaseException instead of Exception, so it sits alongside SystemExit/KeyboardInterrupt and can slip past ordinary except Exception: handlers. Fix: inherit from `Exception`.What's wrong with a custom __init__ that stores self.shortfall = ... but never calls super().__init__(...)?
.args is never populated, so str(e) / print(e) shows an empty message even though your attributes exist. Fix: call super().__init__(message) — see Classes and __init__.What's the bug in except Exception: pass used "to be safe"?
It silently swallows every error, including real bugs you never anticipated, so failures vanish without a trace. Fix: catch the narrowest type and re-raise what you can't handle.
Find the error: except ValueError as e: return e.
Returning the caught exception turns a loud failure back into a quiet value the caller may ignore. If you truly can't handle it, use a bare
raise (or raise ... from e), don't return it.What's wrong with raise ConfigError("bad port") inside an except ValueError: block, given the goal is to keep the original cause visible?
Without
from e, the original ValueError is only shown as "during handling of the above, another exception occurred" (implicit context), not as the direct cause. Use raise ConfigError("bad port") from e to set __cause__ explicitly.Spot the mistake: raise written at the top level of a module (not inside any except).
A bare
raise with no active exception being handled throws RuntimeError: No active exception to re-raise. Bare raise only makes sense inside an except block.Why questions
Why prefer raising an exception over returning a sentinel like -1 or None for an invalid withdraw amount?
A returned sentinel can be silently ignored, letting corrupt state flow onward; raising forces the caller to either handle it or crash loudly, which is safer than a quiet wrong answer.
Why create InsufficientFundsError instead of just raising ValueError?
A named custom class is a catchable category: callers can write
except InsufficientFundsError: to handle that exact situation differently from an unrelated ValueError like a typo.Why does a custom exception often carry attributes (balance, requested) instead of only a message string?
So the handler can read structured data (
e.shortfall) and make decisions, rather than parsing a human-readable sentence — data is programmable, prose is not.Why use a bare raise to re-raise rather than raise <the same exception> again?
A bare
raise preserves the original traceback pointing at where the error first occurred; re-raising a fresh object resets the traceback to the current line and loses that history.Why is assert not a replacement for raise in production input-validation?
assert statements are stripped when Python runs with -O (optimized) mode, so the check disappears; use an explicit raise for validation that must always run. See assert statement.Why is the log-and-re-raise pattern preferred over log-and-swallow?
Logging records that the problem happened, but re-raising admits "I'm not qualified to fix this" and lets a higher layer decide — swallowing would hide the failure while pretending it was handled. See Logging in Python.
Edge cases
What does str(e) return for raise ValueError (class, no message)?
An empty string, because
.args is empty — there was no message to store. Contrast with raise ValueError("x"), where str(e) is "x".What happens to a finally block when an exception is raised in its try and never caught?
The
finally block still runs (cleanup is guaranteed) before the exception continues propagating upward. See Try-Except-Finally.If an exception is raised inside an except block (while handling another), what does Python show?
It reports the new exception with "During handling of the above exception, another exception occurred" — this is implicit chaining via
__context__, distinct from the explicit from chaining via __cause__.What matches first: except ValueError or except Exception, when both are listed and a ValueError is raised?
The first matching clause wins in top-to-bottom order, so put the narrower
except ValueError before the broader except Exception, or the broad one will grab it first.If you raise a custom exception whose parent is ValueError (not Exception), can except ValueError: catch it?
Yes —
except matches by inheritance, so any subclass of ValueError is caught by except ValueError:. Choosing a specific built-in parent is a deliberate way to stay compatible with existing handlers.What does raise None from something or raise SomeException from None do?
from None suppresses chaining, so the traceback won't show the "direct cause" line — useful when the low-level error is noise you intentionally want to hide from the final report.Connections
- Try-Except-Finally — how these raised exceptions get caught and cleaned up
- Exception Hierarchy & BaseException — why the parent class choice matters
- Classes and __init__ — where
super().__init__lives - Logging in Python — the log-and-re-raise pattern
- assert statement — the lighter cousin that can be optimized away