Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions docs/writing/structure.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you mind adding "at runtime" after "to an action"/

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not necessarily running functions (as you show with the second example you provided).

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you please update this to say

Perhaps the most well known example ...

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove "Anyone familiar with this pattern knows that". That alienates people who do not already know and those people are a target audience for this document.

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's reword this sentence to say:

This reduces the number of things a developer has to think about and track while also making the code easier to reason about and read.


There are two easy ways to implement this functionality yourself: using a class

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's remove "easy" from this sentence. It alienates new developers who are the target audience for this document.

or using a generator. Let's implement the above functionality ourselves, starting
with the class approach:

.. code-block:: python

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This example does not explain the parameters to the exit method. Either turn those into *args or explain them below.


class CustomOpen(object):
def __init__(self, filename):
self.file = open(filename)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not indented appropriately.


def __enter__(self):
return self.file

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This works but I do not believe it is a practice that we should encourage (returning something other than the object you're instantiating as the value in the context manager). I've never seen this pattern before. Can you point to projects using anything like this for an object?


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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You probably wanted double-backticks around "CustomOpen"

``__enter__`` method is called and whatever ``__enter__`` returns is assigned to

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace "assigned" with "bound"

``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 <https://docs.python.org/2/library/contextlib.html>`_:

.. 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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace "albeit" with "but"

more terse. The ``custom_open`` function executes until it reaches the ``yield``

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"terser" not "more terse"

statement. It then gives control back to the ``with`` statement, which assigns

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove "back" here please.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"assigns" -> "binds"

whatever was ``yield``'ed to `f` in the ``as f`` portion.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whatever was returned by ``yield`` to ``f``.

(Make sure the "f" has double backticks around it.)


Since the two approaches appear the same, we should follow the Zen of Python

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace

we should follow the Zen of Python

with

a developer is expected to use their judgment

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
--------------

Expand Down