Skip to content

With, match and else Blockđź”—

  • The with statement sets up a temporary context and reliably tears it down, under the control of a context manager object.

Context Managers and with Blocksđź”—

  • Conext Manager objects exist to control a with statement, just like iterators exist to control a for statement.
  • with statement simplifies common use cases of try/finally, which guarantees taht some operation is performed after a block of code, even if the block is terminated by return, and exception or a sys.exit() call. The code in finally cause usually releases a critical resource or restores some previous state that was temporarily changed.
  • Other application
    • Managing transactions in the sqlite3 module
    • Safely handling locks, conditions, and semaphores
    • Setting up custom environments for arithmetic operations in Decimal objects
    • Patching object for testing
  • Context manger interface consists of the __enter__(usually returns self) and __exit__ methods.
with open('mirror.py') as fp:
  src = fp.read(60)

len(src)    # 60
  • Here open() function returns an instance of TextIOWrapper, and its __enter__ method returns self.
  • NOTE: when control exits the with block in any way, the __exit__ method is invoked on the context manager object not on the whatever returned by __enter__
  • NOTE: as clause of the with is optional. In case of open we get reference to file, many context manager will return None which is not useful.
from mirror import LookingGlass
with LookingGlass() as what:    # context manager is an instance of LookingGlass
  print("Alice, Kitty and Snowdrop")    # this print is rigged to print reverse
  print(what)
import sys

class LookingGlass:

    def __enter__(self): # no args
        self.original_write = sys.stdout.write # monkey_patch
        sys.stdout.write = self.reverse_write
        return 'JABBERWOCKY' # return assigned to variable that uses as

    def reverse_write(self, text): # reverse function
        self.original_write(text[::-1])

    def __exit__(self, exc_type, exc_value, traceback): # tear down call
        sys.stdout.write = self.original_write # on exit restore monkey patched func
        if exc_type is ZeroDivisionError: # error handle
            print('Please DO NOT divide by zero!')
            return True  9
        10
  • __exit__ is called with None, None, None if all goes well. These are exc_type (Exception Class), exc_value (Exception instance) and traceback object

The contextlib Utilitiesđź”—

  • Before creating your own context manager classes take a look at contextlib, maybe you can find something already implemented.
  • The contextlib package also includes :
    • closing : A function to build context managers out of objects that provide close() method but don’t implement __enter__/__exit__ interface.
    • suppress: A context manager to temporarily ignore exceptions given as arguments.
    • nullcontext : A context manager that does nothing to simplify conditional logic around object tthat may not implement context manager. It serves as a stand-in when conditional code before with block may or may not provide a context manager for the with statements.
  • The contextlib module provides classes and a decorator that are more widely applicable than decorator
    • @contextmanager : A simple decorater that lets you build context manager from a simple generator functions.
    • AbstractContextManager : An ABC that formalizes the context manager interface, and makes it but easier to create context manager classes by subclassing.
    • ContextDecorator : A base class for defining class-based context manager that can also be used as function decorators, running the entire function within a managed context.
    • ExitStack : A context manager that lets you enter a variable number of context managers. When the with block ends, ExitStack calls the stacked context manager’ __exit__ methods in LIFO order.

Using @contextmangerđź”—

The @contextmanager decorator is an elegant and practical tools that brings three distinctive python features ; a function decorator, a generator, and the with Statement.

# mirror.py
import contextlib
import sys

@contextlib.contextmanager # contextmanager decorator
def looking_glass():
    original_write = sys.stdout.write # preserve original method

    def reverse_write(text): # reverse function
        original_write(text[::-1])

    # note error handling is skipped ! Its full implementation is in book.
    sys.stdout.write = reverse_write # monkey patch
    yield 'JABBERWOCKY' # yield the value bound to target var of as clause
    sys.stdout.write = original_write # undo monkey patch

# A feature of @contextmanager is the generators decorated can also be used as decorators
@looking_glass():
  def verse():
    print("The time has come")

# Output
verse()
# emoc sah emit ehT
print('back to normal')
# back to normal

Pattern Matching in lis.py: A case studyđź”—

Read in Book!

Do This, then That: else Blocks Beyond ifđź”—

else blocks can be used not only with if construct but with for , while and try

  • for : The else block will run only if and when the for loop runs to completed. (i.e. not if the for is aborted with a break)
  • while: The else block will run only if and when the while loop exits because the condition became falsy (i.e., not if the while is aborted with a break).
  • try: The else block will run only if no exception is raised in the try block. The official docs also state: “Exceptions in the else clause are not handled by the preceding except clauses.”

In Python, try/except is commonly used for control flow, and not just for error handling. There’s even an acronym/slogan for that documented in the official Python glossary:

EAFP

    Easier to ask for forgiveness than permission. This common Python coding style assumes the existence of valid keys or attributes and catches exceptions if the assumption proves false. This clean and fast style is characterized by the presence of many try and except statements. The technique contrasts with the LBYL style common to many other languages such as C.

The glossary then defines LBYL:

LBYL

    Look before you leap. This coding style explicitly tests for pre-conditions before making calls or lookups. This style contrasts with the EAFP approach and is characterized by the presence of many if statements. In a multi-threaded environment, the LBYL approach can risk introducing a race condition between “the looking” and “the leaping.” For example, the code, if key in mapping: return mapping[key] can fail if another thread removes key from mapping after the test, but before the lookup. This issue can be solved with locks or by using the EAFP approach.

Given the EAFP style, it makes even more sense to know and use else blocks well in try/except statements.