Skip to main content

Context Managers and else Blocks

Source: Fluent Python, 2nd Edition — Chapter 18 by Luciano Ramalho

Hettinger: with is not the shallow with of BASIC. It is setup and teardown, as fundamental as a subroutine. This chapter also covers else on for / while / try (a then-clause wearing the wrong keyword) and a short note on match OR-patterns. A full Scheme interpreter is a book-length case study; we keep pattern matching to the | rule.

else beyond if​

Read it as then, not “otherwise”:

  • for/else: else runs if the loop finished (no break).
  • while/else: else runs if the condition went falsy (no break).
  • try/else: else runs if the try body raised nothing. Exceptions in else are not caught by the preceding except.

return / break / continue / an exception skip else.

Banana hunt without a flag:

for item in my_list:
if item == 'banana':
break
else:
raise ValueError('no banana')

Put only the risky call in try; put the “success path” in else so you are not accidentally catching errors from after_call().

EAFP (easier to ask forgiveness): try, then except. LBYL (look before you leap): many ifs. LBYL races in threads (if key in d: d[key]). EAFP is why try/else matters.

Context managers and with​

with is try/finally with an object in charge. __enter__ at the start; __exit__ at the end (exception, return, sys.exit, or a clean finish).

The object after with is the manager. as name binds whatever __enter__ returns — often self (open), sometimes another object, sometimes None. __exit__ always runs on the manager, not on the as value.

__exit__(exc_type, exc_value, traceback) gets three Nones on success. Return True to swallow the exception; anything else (including implicit None) lets it propagate.

Uses: files, sqlite transactions, threading locks, decimal.localcontext, unittest.mock.patch. contextlib.redirect_stdout / redirect_stderr swap streams for a block.

You can call __enter__ / __exit__ by hand to see the protocol.

Quiz

The as target

1 / 1

with Mgr() as x binds x to

Pick an answer to see if you are right.

contextlib​

Look here before writing a class: closing (objects with .close only), suppress, nullcontext (3.7, for optional managers), @contextmanager, AbstractContextManager (3.6), ContextDecorator (also a decorator), ExitStack (enter N managers, exit LIFO). 3.7+ async twins for async with.

@contextmanager​

One yield: code before is __enter__; the yielded value is the as target; code after is __exit__. The decorator wraps the generator in a helper class (next to the yield; on the way out throw if there was an exception, else next).

Always try/finally around yield — users can do anything in the block. Default vs a class: @contextmanager suppresses exceptions sent into the generator unless you re-raise. A class __exit__ suppresses only if it returns True.

That yield is a coroutine pause, not iteration. Pieters’ inplace file rewriter: backup and two handles before yield; restore on failure after.

OR-patterns in match​

case int(x) | float(x): matches either class; all branches must bind the same names. | here is not bitwise or. Class pattern int(x) is isinstance plus capture.

The number case is optional; the tests only hit signs and other.