try:
result = 10 / 0
except ZeroDivisionError:
print("Cannot divide by zero")
except ValueError:
print("Invalid value")
Topics
20
Async Generators & Async Context Managers
Command-Line Interfaces (argparse)
Comprehensions & Generators
Concurrency (asyncio/threading/multiprocessing)
Data Types & Structures
Dataclasses & NamedTuples
Debugging & Profiling
Decorators
Descriptors & Properties
Exception Handling
File I/O & Context Managers
Functions & Scope
functools & Functional Programming Tools
Iterators & the Iterator Protocol
Logging
Magic Methods & Operator Overloading
Memory Management & Garbage Collection
Metaclasses & Class Customization
Modules & Packaging
Multiple Inheritance & MRO
Exception Handling
15 questions found
Code in 'try' runs normally; if it raises an exception, Python looks for the FIRST 'except' clause matching that exception's type (checked top to bottom) and runs it — if none match, the exception propagates up the call stack, potentially crashing the program if never caught anywhere.
Real-world example
Gracefully handling a division-by-zero or invalid-input error instead of letting the program crash with an unhandled traceback.
Data Types & Structures
Code in 'finally' ALWAYS runs after try (and any except) completes, whether an exception occurred or not, and even if the try block contains a 'return' — making it the standard place for cleanup code like closing a file or releasing a lock.
file = open("data.txt")
try:
process(file)
finally:
file.close() # always runs, guaranteeing the file is closed
Real-world example
Guaranteeing a file handle or network connection is properly closed regardless of whether processing succeeded or raised.
File I/O & Context Managers
List the exception types as a TUPLE after 'except', separated by commas — the clause catches ANY of the listed types, running the same handling code regardless of which specific one was raised.
try:
value = int(user_input)
result = 10 / value
except (ValueError, ZeroDivisionError) as e:
print(f"Invalid input: {e}")
Real-world example
Handling several related error conditions (like invalid input format AND division by zero) with the same recovery logic.
Exception Handling
How do you create a custom exception class, and what's the minimal amount of code needed?
IntermediateDefine a class inheriting from Exception (or a more specific built-in exception), typically with no additional code needed at all (just 'pass') unless you want to add custom attributes or override __init__ — Python's exception machinery handles the rest automatically via inheritance.
class InsufficientFundsError(Exception):
def __init__(self, shortfall):
super().__init__(f"Insufficient funds. Short by ${shortfall:.2f}")
self.shortfall = shortfall
try:
raise InsufficientFundsError(50.00)
except InsufficientFundsError as e:
print(e.shortfall) # 50.0
Real-world example
Raising a domain-specific exception (like InsufficientFundsError) that carries meaningful business context beyond a generic message.
OOP
What is the difference between 'raise' and 'raise ... from ...' for exception chaining?
IntermediatePlain 'raise NewException()' inside an except block still shows BOTH the original and new exception in the traceback (implicit chaining, labeled 'During handling of the above exception...'); 'raise NewException() from original_exception' makes the chaining EXPLICIT, and 'raise NewException() from None' SUPPRESSES the original exception's context entirely, showing only the new one.
try:
int("not a number")
except ValueError as e:
raise RuntimeError("Failed to parse config") from e # explicit chaining: shows both, labeled 'direct cause'
try:
int("not a number")
except ValueError:
raise RuntimeError("Failed to parse config") from None # suppresses the ValueError entirely
Real-world example
Wrapping a low-level parsing error in a more meaningful, higher-level exception while preserving (or intentionally hiding) the original cause.
Debugging & Profiling
How do you access details about the exception currently being handled, like its type, value, and traceback, using sys.exc_info()?
Intermediatesys.exc_info() returns a 3-tuple (exception type, exception instance, traceback object) for the exception CURRENTLY being handled — mostly superseded by simply using 'except Exception as e' to directly capture the instance, but still useful for lower-level introspection or re-raising logic.
import sys
try:
1 / 0
except ZeroDivisionError:
exc_type, exc_value, exc_tb = sys.exc_info()
print(exc_type) # <class 'ZeroDivisionError'>
print(exc_value) # division by zero
Real-world example
Building generic exception-logging or reporting middleware that needs low-level access to full exception details.
Debugging & Profiling
How does the 'else' clause on a try statement work, and what's its purpose compared to just putting that code at the end of 'try'?
IntermediateThe 'else' clause runs ONLY if the 'try' block completed WITHOUT raising any exception — putting code there (instead of at the end of 'try') makes it explicit that this code is NOT meant to be protected by the except clauses, avoiding accidentally catching exceptions from code that wasn't the original risky operation.
try:
value = risky_parse(data)
except ValueError:
print("Parsing failed")
else:
print(f"Parsed successfully: {value}") # only runs if risky_parse succeeded, and its own errors aren't caught above
Real-world example
Clearly separating the 'risky' operation being protected from subsequent code that shouldn't accidentally have its own errors caught by the same except clause.
Debugging & Profiling
How does Python 3.11's exception groups (ExceptionGroup) and the 'except*' syntax let you handle MULTIPLE unrelated exceptions raised together?
AdvancedExceptionGroup wraps multiple exceptions raised together (common in concurrent code, like asyncio.gather with multiple failures) as a single object; 'except*' matches and handles the SUBSET of exceptions within the group matching a given type, potentially re-raising a NEW ExceptionGroup with the remaining unmatched ones.
try:
raise ExceptionGroup("multiple failures", [ValueError("bad value"), TypeError("bad type")])
except* ValueError as eg:
print(f"Handled ValueErrors: {eg.exceptions}")
except* TypeError as eg:
print(f"Handled TypeErrors: {eg.exceptions}")
Real-world example
Handling multiple independent failures from concurrent tasks (like several parallel API calls) that fail simultaneously with different exception types.
Concurrency (asyncio/threading/multiprocessing)
How would you design a custom exception HIERARCHY for an application, and why does this matter for how callers can catch errors at different granularities?
AdvancedCreate a base application exception (e.g., AppError), then specific subclasses for different error categories (e.g., ValidationError, NotFoundError, all inheriting from AppError) — callers can catch broadly (`except AppError`) to handle ALL application errors generically, or narrowly (`except ValidationError`) for specific recovery logic, without needing to know every specific subclass upfront.
class AppError(Exception):
pass
class ValidationError(AppError):
pass
class NotFoundError(AppError):
pass
try:
validate_and_fetch(data)
except ValidationError:
return {"error": "invalid input"}, 400
except AppError:
return {"error": "application error"}, 500 # catches NotFoundError and any FUTURE AppError subclass too
Real-world example
Designing a web API's error-handling layer so new exception types added later are automatically caught by broader existing handlers.
OOP
How does context manager __exit__ interact with exception handling, and how can it SUPPRESS an exception by returning True?
Advanced__exit__(self, exc_type, exc_value, traceback) receives details of any exception raised inside the 'with' block; if it returns a TRUTHY value, the exception is SUPPRESSED entirely (treated as handled, execution continues after the 'with' block) — returning None/False (the default) lets the exception propagate normally.
class SuppressErrors:
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, tb):
if exc_type is ValueError:
print(f"Suppressed: {exc_value}")
return True # suppresses the ValueError
return False # lets other exception types propagate normally
with SuppressErrors():
raise ValueError("this gets suppressed")
print("Execution continues here")
Real-world example
Building a custom context manager (similar to contextlib.suppress) that intentionally swallows specific, expected exception types.
File I/O & Context Managers
Showing 1–10 of 15