From 77cf4f478d4effec4833e211e6bb76ff54960cef Mon Sep 17 00:00:00 2001 From: sirMackk Date: Thu, 22 Oct 2015 07:55:18 +0200 Subject: [PATCH] Adds context manager section to structure.rst --- docs/writing/structure.rst | 69 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/docs/writing/structure.rst b/docs/writing/structure.rst index 05a90e8..7d9a062 100644 --- a/docs/writing/structure.rst +++ b/docs/writing/structure.rst @@ -316,6 +316,75 @@ expensive function in a table and use them directly instead of recomputing them when they have already been computed. This is clearly not part of the function logic. +Context Managers +---------------- + +A context manager is a Python object that provides extra contextual information +to an action. This extra information takes the form of running a function upon +initiating the context using the ``with`` statement as well as running a function +upon completing all the code inside the ``with`` block. The most well known +example of using a context manager is operating on a file: + +.. code-block:: python + + with open('file.txt') as f: + contents = f.read() + +Anyone familiar with this pattern knows that invoking ``open`` in this fashion +ensures that ``f``'s ``close`` method will be called at some point. This reduces +a developer's cognitive load and makes code easier to read. + +There are two easy ways to implement this functionality yourself: using a class +or using a generator. Let's implement the above functionality ourselves, starting +with the class approach: + +.. code-block:: python + + class CustomOpen(object): + def __init__(self, filename): + self.file = open(filename) + + def __enter__(self): + return self.file + + def __exit__(self, ctx_type, ctx_value, ctx_traceback): + self.file.close() + + with CustomOpen('file') as f: + contents = f.read() + +This is just a regular Python object with two extra methods that are used +by the ``with`` statement. CustomOpen is first instantiated and then its +``__enter__`` method is called and whatever ``__enter__`` returns is assigned to +``f`` in the ``as f`` part of the statement. When the contents of the ``with`` block +is finished executing, the ``__exit__`` method is then called. + +And now the generator approach using Python's own +`contextlib `_: + +.. code-block:: python + + from contextlib import contextmanager + + @contextmanager + def custom_open(filename): + f = open(filename) + yield f + f.close() + + with custom_open('file') as f: + contents = f.read() + +This works in exactly the same way as the class example above, albeit it's +more terse. The ``custom_open`` function executes until it reaches the ``yield`` +statement. It then gives control back to the ``with`` statement, which assigns +whatever was ``yield``'ed to `f` in the ``as f`` portion. + +Since the two approaches appear the same, we should follow the Zen of Python +to decide when to use which. The class approach might be better if there's +a considerable amount of logic to encapsulate. The function approach +might be better for situations where we're dealing with a simple action. + Dynamic typing --------------