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 afor
statement. with
statement simplifies common use cases oftry/finally
, which guarantees taht some operation is performed after a block of code, even if the block is terminated byreturn
, and exception or asys.exit()
call. The code infinally
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
- Managing transactions in the
- Context manger interface consists of the
__enter__
(usually returnsself
) and__exit__
methods.
- Here
open()
function returns an instance ofTextIOWrapper
, and its__enter__
method returnsself
. - 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 thewith
is optional. In case ofopen
we get reference to file, many context manager will returnNone
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 withNone
,None
,None
if all goes well. These areexc_type
(Exception Class),exc_value
(Exception instance) andtraceback
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 provideclose()
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 beforewith
block may or may not provide a context manager for thewith
statements.
- The
contextlib
module provides classes and a decorator that are more widely applicable thandecorator
@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 thewith
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
: Theelse
block will run only if and when thefor
loop runs to completed. (i.e. not if thefor
is aborted with a break)while
: Theelse
block will run only if and when thewhile
loop exits because the condition became falsy (i.e., not if thewhile
is aborted with abreak
).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.