Documenting your code

Most people think of writing documentation as an unpleasant, but necessary task, done for the benefit of othe people with no real benefit to themselves. So they choose not to do it, or they do it with little care.

But even if you are the only person who will ever use your code, it’s still a good idea to document it well. Being able to document your own code gives you confidence that you understand it yourself, and a sign of well-written code is that it can be easily documented. Code you wrote a few weeks ago may as well have been written by someone else, and you will be glad that you documented it.

The good news is that writing documentation can be fun, and you really don’t need to write a lot of it.

Docstrings and comments

Documentation is not comments.

A docstring in Python is a string literal that appears at the beginning of a module, function, class, or method.

"""
A docstring in Python that appears
at the beginning of a module, function, class or method.
"""

The docstring of a module, function, class or method becomes the __doc__ attribute of that object, and is printed if you type help(object):

In [1]: def fahr_to_celsius(F):
   ...:     """
   ...:     Convert temperature from Fahrenheit to Celsius.
   ...:     """
   ...:     return (F - 32) * (5/9)

In [2]: help(fahr_to_celsius)

Help on function fahr_to_celsius in module __main__:

fahr_to_celsius(F)
 Convert temperature from Fahrenheit to Celsius.

A comment in Python is any line that begins with a #:

# a comment.

The purpose of a docstring is to document a module, function, class, or method. The purpose of a comment is to explain a very difficult piece of code, or to justify a choice that was made while writing it.

Docstrings should not be used in place of comments, or vice versa. Don’t do the following:

In [1]: def fahr_to_celsius(F):
   ...:     # Convert temperature from Fahrenheit to Celsius.
   ...:     return (F - 32) * (5/9)

Deleting code

Incidentally, many people use comments and string literals as a way of “deleting” code - also known as commenting out code. See this article on a better way to delete code.

What to document?

So what goes in a dosctring?

At minimum, the docstring for a function or method should consist of the following:

  1. A Summary section that describes in a sentence or two what the function does.
  2. A Parameters section that provides a description of the parameters to the function, their types, and default values (in the case of optional arguments).
  3. A Returns section that similarly describes the return values.
  4. Optionally, a Notes section that describes the implementation, and includes references.

Here is a simple example of this in action:

def flip_list(a, inplace=False):
    """
    Flip (reverse) a list.

    Parameters
    ----------
    a : list 
        List to be reversed.
    inplace : bool, optional
        Specifies whether to flip the list "in place",
        or return a new list (default).

    Returns
    -------
    flipped : list (or None)
        The flipped list. If `inplace=True`, None is returned.
    """
    if inplace is True:
        a[:] = a[::-1]
        return None
    else:
        return a[::-1]

NumPy’s documentation guidelines are a great reference for more information about what and how to document your code.

Doctests

In addition to the sections above, your documentation can also contain runnable tests. This is possible using the doctest module.

flip_list.py
def flip_list(a, inplace=False):
    """
    Flip (reverse) a list.

    Parameters
    ----------
    a : list 
        List to be reversed.
    inplace : bool, optional
        Specifies whether to flip the list "in place",
        or return a new list (default).

    Returns
    -------
    flipped : list (or None)
        The flipped list. If `inplace=True`, None is returned.


    >>> flip_list([1, 2, 3])
    [3, 2, 1]

    >>> a = [1, 2, 3]
    >>> flip_list(a, inplace=True)
    >>> a
    [3, 2, 1]
    """
    if inplace is True:
        a[:] = a[::-1]
        return None
    else:
        return a[::-1]

You can tell pytest to run doctests as well as other tests using the --doctest-modules switch:

$ pytest --doctest-modules flip_list.py

collected 1 item

flip_list.py .                                                            [100%]

=========================== 1 passed in 0.03 seconds ===========================

Doctests are great because they double up as documentation as well as tests. But they shouldn’t be the only kind of tests you write.

Documentation generation

Finally, you can turn your documentation into a beautiful website (like this one!), a PDF manual, and various other formats, using a document generator such as Sphinx. You can use services like readthedocs to build and host your website for free.