Libraries and Modules in Python

Documentation

Overview:

  • Teaching: 10 min
  • Exercises: 5 min

Questions

  • Why should I document my code?
  • What should I put in a docstring?

Objectives

  • Know how to document Python functions
  • Understand how to follow best practice in documentation to create reusable code

Python has great in-built documentation that is available via the help function. For example

In [1]:
l = ["cat", "dog", "fish"]
In [2]:
help(l)
Help on list object:

class list(object)
 |  list(iterable=(), /)
 |  
 |  Built-in mutable sequence.
 |  
 |  If no argument is given, the constructor creates a new empty list.
 |  The argument must be an iterable if specified.
 |  
 |  Methods defined here:
 |  
 |  __add__(self, value, /)
 |      Return self+value.
 |  
 |  __contains__(self, key, /)
 |      Return key in self.
 |  
 |  __delitem__(self, key, /)
 |      Delete self[key].
 |  
 |  __eq__(self, value, /)
 |      Return self==value.
 |  
 |  __ge__(self, value, /)
 |      Return self>=value.
 |  
 |  __getattribute__(self, name, /)
 |      Return getattr(self, name).
 |  
 |  __getitem__(...)
 |      x.__getitem__(y) <==> x[y]
 |  
 |  __gt__(self, value, /)
 |      Return self>value.
 |  
 |  __iadd__(self, value, /)
 |      Implement self+=value.
 |  
 |  __imul__(self, value, /)
 |      Implement self*=value.
 |  
 |  __init__(self, /, *args, **kwargs)
 |      Initialize self.  See help(type(self)) for accurate signature.
 |  
 |  __iter__(self, /)
 |      Implement iter(self).
 |  
 |  __le__(self, value, /)
 |      Return self<=value.
 |  
 |  __len__(self, /)
 |      Return len(self).
 |  
 |  __lt__(self, value, /)
 |      Return self<value.
 |  
 |  __mul__(self, value, /)
 |      Return self*value.
 |  
 |  __ne__(self, value, /)
 |      Return self!=value.
 |  
 |  __repr__(self, /)
 |      Return repr(self).
 |  
 |  __reversed__(self, /)
 |      Return a reverse iterator over the list.
 |  
 |  __rmul__(self, value, /)
 |      Return value*self.
 |  
 |  __setitem__(self, key, value, /)
 |      Set self[key] to value.
 |  
 |  __sizeof__(self, /)
 |      Return the size of the list in memory, in bytes.
 |  
 |  append(self, object, /)
 |      Append object to the end of the list.
 |  
 |  clear(self, /)
 |      Remove all items from list.
 |  
 |  copy(self, /)
 |      Return a shallow copy of the list.
 |  
 |  count(self, value, /)
 |      Return number of occurrences of value.
 |  
 |  extend(self, iterable, /)
 |      Extend list by appending elements from the iterable.
 |  
 |  index(self, value, start=0, stop=9223372036854775807, /)
 |      Return first index of value.
 |      
 |      Raises ValueError if the value is not present.
 |  
 |  insert(self, index, object, /)
 |      Insert object before index.
 |  
 |  pop(self, index=-1, /)
 |      Remove and return item at index (default last).
 |      
 |      Raises IndexError if list is empty or index is out of range.
 |  
 |  remove(self, value, /)
 |      Remove first occurrence of value.
 |      
 |      Raises ValueError if the value is not present.
 |  
 |  reverse(self, /)
 |      Reverse *IN PLACE*.
 |  
 |  sort(self, /, *, key=None, reverse=False)
 |      Stable sort *IN PLACE*.
 |  
 |  ----------------------------------------------------------------------
 |  Static methods defined here:
 |  
 |  __new__(*args, **kwargs) from builtins.type
 |      Create and return a new object.  See help(type) for accurate signature.
 |  
 |  ----------------------------------------------------------------------
 |  Data and other attributes defined here:
 |  
 |  __hash__ = None

You can add similar documentation to the functions that you write. You do this by adding in a documentation string as the first string after defining the function e.g.

In [3]:
def multiply(a, b):
    """This function returns the element-wise multiplication of the passed lists 'a' and 'b'"""
    c = []
    for x,y in zip(a,b):
        c.append(x*y)
    return c
In [4]:
multiply( [1,2,3], [4,5,6] )
Out[4]:
[4, 10, 18]
In [5]:
help(multiply)
Help on function multiply in module __main__:

multiply(a, b)
    This function returns the element-wise multiplication of the passed lists 'a' and 'b'

The documentation string should be placed between two sets of triple quotes ("""). This is a convention that makes it easier to expand the documentation later, and that ensures that nothing you write in the documentation will be expanded or interpreted as Python.

Documentation should provide an easy to understand, and brief description of what the function does. It should not give information that is obvious by reading the function signature. For example, this is a bad piece of documentation.

In [6]:
def multiply(a, b):
    """function multiply(a,b) -> list"""
    c = []
    for x,y in zip(a,b):
        c.append(x*y)
    return c
In [7]:
help(multiply)
Help on function multiply in module __main__:

multiply(a, b)
    function multiply(a,b) -> list

It is much better to say what the function does, and then what it returns (as this can't be seen from the signature). Good documentation would be

In [8]:
def multiply(a, b):
    """Calculates the element-wise multiplication of a and b, returning a list of the results"""
    c = []
    for x,y in zip(a,b):
        c.append(x*y)
    return c
In [9]:
help(multiply)
Help on function multiply in module __main__:

multiply(a, b)
    Calculates the element-wise multiplication of a and b, returning a list of the results

Your documentation can span over multiple lines. If you are describing the arguments, then you should use one line per argument, for example

In [10]:
def make_complex(real, imag=0):
    """Create and return a complex number
    
       Keyword arguments:
       
       real -- the real part of the number
       imag -- the imaginary part of the number
    """
    return (real,imag)
In [11]:
help(make_complex)
Help on function make_complex in module __main__:

make_complex(real, imag=0)
    Create and return a complex number
    
    Keyword arguments:
    
    real -- the real part of the number
    imag -- the imaginary part of the number

By convention, you will notice above that the last """ is placed on its own line if the documentation spans multiple lines. It is on the same line if the documentation is short.

In general, keep your documentation short, to the point, and avoid repeating obvious information. However, be precise, as this may be the only part of your code that somebody else reads before they use your function in their program.

A good suggestion is to look at documentation you like and try to copy that style. Also, look for code that you think is poorly documented, and try to avoid their mistakes. An example guide is PEP257

Add Doctrings

Below is a series of undocumented functions. Take a look through the functions and try to work out what they do. Once you understand the functions, write some documentation for each function. Get your neighbour to read your documentation. Do they understand what the function does based on what you have written? Do the function names combined with your documentation accurately convey the result of calling the function?

Note that you may have to use help(...) yourself if you don't recognise some of the code in the functions. Also try to play with the function to see how it behaves.

In [12]:
def add(a, b):
    c = []
    for x,y in zip(a,b):
        c.append(x+y)
    return c
In [13]:
def subtract(a, b):
    c = []
    for x,y in zip(a,b):
        c.append(x / y)
    return c
In [14]:
def capitalise(message):
    words = message.split(" ")
    for i in range(0,len(words)):
        words[i] = "%s%s" % (words[i][0].upper(), words[i][1:])
    return " ".join(words)
In [15]:
def surprise(x):
    import random
    if x < random.random():
        print("Surprise!")

For this last function, try calling it via list_interface("ipynb").

In [16]:
def list_interface(x):
    import glob
    f = glob.glob("*.%s" % x)
    l = []
    for x in f:
        if x.startswith("0"):
            l.append(x)
    return l

Key Points:

  • Python Docstrings are an efficient way of documenting your code
  • Python's help function automatically documnetation from your docstrings
  • Use good formatting to help your users (and your future self) use your code
  • Use good practice in code you use or follow coding style when you contribute to projects