After this lesson, you will be able to: Use exceptions correctly, define custom exceptions, write context managers for cleanup, and log instead of print.
Error handling separates code that breaks loudly from code that breaks silently. The latter is worse.
Catch specific, raise specific, never silence.
# Specific catchestry:user = User.load(user_id)except FileNotFoundError:return Noneexcept PermissionError:log.error("perm denied")raise# Custom exception (informative type beats string parsing)class UserNotFound(Exception):def __init__(self, user_id: str):super().__init__(f"user {user_id} not found")self.user_id = user_id# Try/finally vs context managertry:f = open("file")data = f.read()finally:f.close()# Betterwith open("file") as f:data = f.read()# raise from (preserve traceback)try:parse(payload)except ValueError as e:raise InvalidPayload(payload) from e
print() is fine for scripts. For anything else, use logging. logging gives you: levels (DEBUG/INFO/WARNING/ERROR/CRITICAL), structured output, file/syslog routing, on-by-default. Pattern: `log = logging.getLogger(__name__)` at module top; `log.info("...")` everywhere.
Drop in main entry point.
import logginglogging.basicConfig(level=logging.INFO,format="%(asctime)s %(levelname)s %(name)s %(message)s",)log = logging.getLogger(__name__)log.info("app started")log.warning("low disk")log.exception("crash") # logs message + traceback automatically inside except
`except:` or `except Exception:` without re-raising. Swallows real bugs. Catching exceptions to print + return None. The caller has no idea what failed. Using print for ops logs. Lost on restart; no levels; no routing. Forgetting `from` clause when re-raising, original cause is gone.
Pick the cleanest answer.
Sign in and purchase access to unlock this lesson.