Handbooks / Python / Chapter 4
Errors and the Standard Library
7 pages · ~5 min✓ Reviewed
Builds on OOP in Python. Next up: Iterators and Generators.
Exception Handling Mental Model
1. Raise, Unwind, Run Order
- An exception is an object raised at runtime
- It unwinds the call stack to the first matching
except - Uncaught: the program ends with a traceback
finallyruns even afterreturn,breakor an uncaught error
- tryrun body
- exceptonly if raised
- elseonly if no error
- finallyalways
python
def f(): try: return "try" finally: print("finally") print(f())
finally runs before the return value is delivered
output
finally try
2. Matching Clauses
- Clauses checked top to bottom: first match wins
- List specific subclasses before parents like
Exception - Tuple catches several:
except (KeyError, IndexError) as e eis deleted when the except block ends
3. Catching Too Much
| Clause | Swallows |
|---|---|
except: / except BaseException | Ctrl+C, SystemExit |
except Exception | Bugs, but not Ctrl+C |
except KeyError | Only that type |
Default
Catch a specific type by default, Exception at most. Never a bare except:.
Raising and Custom Exceptions
1. Raising and Chaining
raise ValueError('msg')raises a new exception- Bare
raiseinsideexceptre-raises the current one, traceback intact
| Form | Sets | Traceback |
|---|---|---|
raise B from A | __cause__ = A | direct cause |
raise B in except | __context__, automatic | during handling |
raise B from None | __cause__ None, __suppress_context__ True | chain hidden, __context__ kept |
2. Custom Exceptions
class AppError(Exception): pass- Subclass Exception, never
BaseException - One base class per library, subclass it per case
- Store fields in
__init__, callsuper().__init__(msg) - Callers read
e.argsand the attributes
python
class AppError(Exception): pass class DbError(AppError): def __init__(self, msg, code): super().__init__(msg) self.code = code try: raise DbError('down', 503) except AppError as e: print(e.args, e.code)
output
('down',) 503
3. Mistakes
Never swallow errors
except Exception: pass hides bugs. Raising a bare string is a TypeError. Print-and-continue loses the failure: log or re-raise.
Collections: Counter, defaultdict, deque
1. Counter
Counter(iterable)counts hashables.most_common(n)gives the top n(item, count)pairs- A missing key gives 0, not
KeyError +and-combine counters;-drops counts of 0 or less
python
from collections import Counter c = Counter('banana') print(c.most_common(2), c['z']) print(c + Counter('an'))
output
[('a', 3), ('n', 2)] 0 Counter({'a': 4, 'n': 3, 'b': 1})
2. defaultdict
defaultdict(list)calls the factory to build a missing value on first accessd[k].append(x)needs no key check- Factory:
int,list,setorlambda: 'n/a' - Reading a missing key inserts it
- Test safely with
d.get(k)ork in d
3. deque
deque(maxlen=n): double-ended queue, O(1)append,appendleft,pop,popleft- A full maxlen deque drops items from the opposite end
| Operation | list | deque |
|---|---|---|
| Remove from front | pop(0): O(n) | popleft(): O(1) |
| Add at front | insert(0, x): O(n) | appendleft(x): O(1) |
| Index in the middle | O(1) | O(n) |
Pick one
Queue: deque. Random access by index: list.
pathlib and json
1. Paths and files
Path('data') / 'file.txt'joins with /Path.cwd(),Path.home();.parent.name.stem.suffixbeat slicingread_text(encoding='utf-8')andwrite_text(s): whole fileexists(),is_file(),glob('*.py'),mkdir(parents=True, exist_ok=True)
python
from pathlib import Path p = Path('out') / 'log.txt' p.parent.mkdir(parents=True, exist_ok=True) p.write_text('hi', encoding='utf-8') print(p.read_text(encoding='utf-8'), p.stem, p.suffix)
output
hi log .txt
2. json
dumps(obj, indent=2)/loads(s): stringsdump(obj, f)/load(f): file objects; the s means string
| Python | JSON |
|---|---|
dict | object |
list, tuple | array |
None, True | null, true |
(1, 2) round trip | list [1, 2] |
{1: 'a'} round trip | string key '1' |
Mistake
dumps raises TypeError on datetime, set, custom objects. Pass default=str or a function. Open json files with encoding='utf-8'.
Typing Basics and the GIL
1. Type Hints
- Hints annotate only; Python never enforces them at runtime
mypyandpyrightcheck them
python
def f(x: int, y: str = 'a') -> bool: return x > 0
| Form | Meaning |
|---|---|
list[int] | list of ints |
dict[str, int] | str keys, int values |
tuple[int, ...] | any length of ints |
int | None | or Optional[int] |
Callable[[int], str] | int in, str out |
Any | escape hatch |
2. The GIL
- Default (GIL) build: one thread runs bytecode at a time
- CPU-bound threads gain no speedup
- Free-threaded build (
python3.14t): no GIL, threads run in parallel - GIL is released while waiting on I/O
| Work | Use |
|---|---|
| I/O-bound | threads |
| CPU-bound | multiprocessing, ProcessPoolExecutor |
3. Common Mistake
GIL does not mean thread-safe
counter += 1 is not atomic. Guard shared state with threading.Lock.
python
with lock: counter += 1
Python Essentials Cheat Sheet
1. Errors
elseruns on success,finallyalwaysexceptorder: specific before general- Bare
raisere-raises;raise X from echains from Nonehides the chain,__context__is still keptclass AppError(Exception); catchException, notBaseException
python
try: run() except KeyError as e: raise AppError("bad") from e else: ok() finally: close()
Never
except: pass hides bugs. Log or re-raise.
2. Collections
| Tool | Remember |
|---|---|
Counter | most_common(n) |
defaultdict | defaultdict(list) |
deque | deque(maxlen=n), O(1) appendleft/popleft |
3. pathlib and json
| Call | Does |
|---|---|
Path('a') / 'b' | join paths |
read_text / write_text | whole file |
mkdir(parents=True, exist_ok=True) | make dirs |
glob('*.py') | match files |
dumps / loads | strings |
dump / load | files |
4. Hints and the GIL
- Hints are unenforced:
int | None,list[int] - Default (GIL) build: threads for I/O, processes for CPU
- Free-threaded build (
python3.14t) has no GIL counter += 1is not atomic: guard shared state withLock
Check yourself
What does this print?
fin, thentry- finally runs before the function actually returns
def f(): try: return 'try' finally: print('fin') print(f())
After d = defaultdict(int) and a read of d['a'], what is len(d)?
1: reading a missing key inserts it- Test safely with
d.get('a')or'a' in d
What does json.loads(json.dumps({1: (2, 3)})) return?
{'1': [2, 3]}- Dict keys become strings and tuples come back as lists