Course Content
Python Essentials for AI Engineer
6 sections · 48 lessons
What is a try-except block?
What you need to know
How matching works
1def to_amount(cell):2 try:3 return float(cell)4 except (TypeError, ValueError) as e: # a tuple catches several types5 print(f"bad cell {cell!r}: {type(e).__name__}")6 return None78print(to_amount("499.5")) # 499.59print(to_amount("N/A")) # bad cell 'N/A': ValueError10 # None11print(to_amount(None)) # bad cell None: TypeError12 # NoneClauses are checked top to bottom, and the first match wins. So put specific exceptions before general ones; except Exception first would hide every clause after it. as e gives you the exception object, with its message and type.
Why not catch everything
- A bare
except:catchesBaseException, includingKeyboardInterruptandSystemExit, so Ctrl+C no longer stops your script. except Exception:is narrower but still catches your own bugs: a typo that raisesNameErroror anAttributeErrorlooks the same as the failure you meant to handle.except Exception: passis the worst form: the bug still happens, but now nobody knows.
If you must catch broadly — at the top of a worker loop, for example — log the full traceback with logging.exception("...") and decide whether to re-raise.
Keep the try small
Wrap only the line that can raise the error you expect. If ten lines sit in the try, a KeyError from line 7 gets handled as if it came from line 2.
Python 3.11+: several errors at once
Concurrent code (for example, asyncio.TaskGroup running many LLM calls) can fail with several exceptions at once, bundled in an ExceptionGroup. Python 3.11 added except* to handle each type inside the group.
A real-life example
A service calls an LLM API. Different failures need different reactions: a timeout or rate limit deserves a retry with backoff; a bad request will fail again, so retrying wastes money.
1import time23class RateLimitError(Exception): pass # real SDKs define errors like these4class BadRequestError(Exception): pass56attempts = {"n": 0}7def call_llm(prompt):8 attempts["n"] += 19 if attempts["n"] == 1: raise TimeoutError("read timed out")10 if attempts["n"] == 2: raise RateLimitError("429 Too Many Requests")11 return f"summary of {prompt!r}"1213def ask(prompt, retries=3):14 for i in range(retries):15 try:16 return call_llm(prompt)17 except (TimeoutError, RateLimitError) as e: # transient: retry18 print(f"attempt {i + 1} failed: {e}")19 time.sleep(0.1 * 2 ** i)20 except BadRequestError: # permanent: don't retry21 raise22 raise RuntimeError(f"gave up after {retries} attempts")2324print(ask("ticket 1142"))25# attempt 1 failed: read timed out26# attempt 2 failed: 429 Too Many Requests27# summary of 'ticket 1142'Each except names exactly what it handles. A typo inside call_llm would raise NameError, which matches neither clause and surfaces immediately instead of being retried three times.
Follow-up questions to expect
- "Can one
exceptcatch several exception types?" — Yes, with a tuple:except (TimeoutError, ConnectionError) as e:. - "What is wrong with a bare
except:?" — It catches everything, includingKeyboardInterruptandSystemExit, and hides real bugs. Catch specific types. - "What is
except*?" — Syntax from Python 3.11 for handling anExceptionGroup, where several exceptions are raised together by concurrent tasks.