Why Document Code?

  1. For sharing code
  2. To organize our current code
  3. To make re-reading old code easier

Programmers write documentation in order to explain what a function does. This is useful not only for sharing code with other programmers, but when we come back to code we wrote later. To document code, you create comments that are ignored when the code is executed. These comments are for the benefit of humans, not the computer. Python has two ways of writing comments.

Single-line Comment

# This line is ignored
a = 0 # The rest of this line is ignored
b = 0

To make a single-line comment in Python, you should use the hash symbol (also known as “pound” or “octothorpe”, written as #). Everything after the hash symbol on the same line is completely ignored by your program. These are often used to document a single line of code. However, you must not use single line comments to create docstrings.

Multi-line Comment

def my_function(x:int, y:int)->int:
    """
    The start of this string needed to be
    indented, otherwise you would have a syntax
    error!
    """

To write multi-line comments in Python, you actually create a triple-quoted string. When Python evaluates this string, the value is unused, so it is safely ignored by the computer. However, unlike the single-line comment, that means that the start of a multi-line comment must respect indentation rules as shown here. However, on the plus side, this triple-quote string counts as the body of the function, so you no longer need the pass statement. Because multi-line comments are just string literal values, they must respect the syntax rules of Python, unlike single-line comments. So, you cannot have a multi-line string value on a line with any other code; they must be on their own separate line.

How Much to Comment?

There are many opinions on when to write comments, and no “correct answer”. Some people believe you need to document every single line. Some people believe you should only document functions and programs as a whole. Eventually, you will need to find your own balance - for example, rather than documenting every line, you might only document lines that are particularly confusing, or document a chunk of code.

Documenting functions

def fix_capitalization(title: str)->str:
    '''
    This function capitalizes the first letter of each
    word in the title, correctly ignoring prepositions.

    Args:
        title (str): A book or movie title
            that we want to fix.
    Returns:
        str: The corrected title
    '''

Many people agree that, at a minimum, you should document all your functions. Use a triple-quoted string as the first line in the definition of the function. Then, give a quick description of what the function does. To give complete documentation, you should also explain what the parameters and returned value mean. Leave a blank line, and then type “Args:”. Indent the next line, and then type the name of the first parameter followed by its type in parentheses. On the same line, write a colon and then describe the parameter. If you need to continue onto the next line, make sure it is indented past the previous line. You should repeat this for all the parameters of your function. Finally, write “Returns:” and then on the next line write the name of the return type, followed by a colon and a description of the returned value.

What Goes Into a Docstring?

  1. Function Purpose: a brief description of what the function does
  2. Parameters: describe what the parameters are and what they represent
  3. Return Value: describe what the return value is and what it represents
  4. Examples: provide examples of how to use the function and what the expected output is

Deciding what to write in the docstring can be tricky. The general guidance is that the reader needs to understand the function’s purpose, how to use it, and what to expect from it. You are not obligated to explain how the function works internally, and that is often a distraction. Instead, you can focus on the inputs and outputs of the function, and any important details about its behavior. Examples can also be helpful in illustrating how to use the function.

Sometimes, if you have good parameter and function names, the user of the function can infer how to use the function from the names and types alone. But often, it’s helpful to explain them in more detail if there are any surprises. If there are potential ambiguities, then it is worth writing extra text to explain this. If the function is straightforward, then a simple description may be enough. The amount of detail you go into should be proportional to the complexity of the function.

Sometimes it is useful to explain the reasoning behind certain decisions or the overall approach taken in the function, or mention “gotchas” that users should be aware of. For example, if a function has a particularly complex algorithm, it might be worth explaining the key steps in the docstring. Or if the function has any side effects (like printing to the console), those should be mentioned as well. However, in general, you can just focus on explaining how to use the function, and not how it works.

When we learned about defining functions, we previously described the parameters’ type and return type as the signature of the function. The fact that it has a special name should reinforce that these are especially important pieces of meta-information.

Missing Docstrings

from bakery import assert_equal

def calculate_area(length: int, width: int) -> int:
    '''
    This function calculates the area of a rectangle based on its dimensions.

    Args:
        length (int): The length of the rectangle in feet.
        width (int): The width of the rectangle in feet.
    Returns:
        int: The area in square feet.
    '''
    area = length * width
    return area

assert_equal(calculate_area(5, 6), 30)
assert_equal(calculate_area(0, 6), 0)
assert_equal(calculate_area(4, 5), 20)

Docstrings are a little unusual compared to regular comments because they are considered part of the source code. Often, beginners will put docstrings in the wrong location: after the function definition, or before. However, a docstring MUST be the first line of the body of the function or it will not count. The docstring must also be a string literal value; an octothorpe (#) will not count. Here is an example of a fully documented function in the context of a bigger program.

Summary