Python Docstrings Best Practices

Python Docstrings Best Practices

If you’re writing Python code that others will read, use, or maintain, you know how important it is to write clear, helpful docstrings. A docstring is not just any comment—it’s a structured piece of documentation that lives inside your code and helps everyone (including your future self!) understand what a function, class, or module does. In this article, we’ll take a deep dive into Python docstring best practices, so you can write documentation that is as elegant and useful as your code.

Let’s start with the basics. In Python, a docstring is a string literal that appears as the first statement in a module, function, class, or method definition. You write it using triple quotes (either single or double), like this:

def greet(name):
    """Display a greeting message."""
    print(f"Hello, {name}!")

This is a simple docstring. It tells the reader that the function greet displays a greeting message. But we can do so much more! A good docstring typically includes:

  • A one-line summary.
  • A more detailed description (if needed).
  • Information about parameters.
  • Information about return values.
  • Any exceptions that might be raised.

There are several conventions for writing docstrings, but the most widely used ones are Google-style, NumPy/SciPy-style, and reStructuredText (Sphinx-style). Let’s look at each of them.

The Google-style docstring is clean, readable, and very popular in the Python community. Here’s an example:

def calculate_area(length, width):
    """Calculate the area of a rectangle.

    Args:
        length (float): The length of the rectangle.
        width (float): The width of the rectangle.

    Returns:
        float: The area of the rectangle.
    """
    return length * width

Notice how it uses sections like Args and Returns to organize information. This makes it easy to read and understand.

The NumPy/SciPy style is another great option, especially common in scientific computing. It looks like this:

def calculate_area(length, width):
    """Calculate the area of a rectangle.

    Parameters
    ----------
    length : float
        The length of the rectangle.
    width : float
        The width of the rectangle.

    Returns
    -------
    float
        The area of the rectangle.
    """
    return length * width

This style uses headings and indentation to create a structured layout. Some people find it more visually organized.

If you’re using Sphinx to generate documentation, you might prefer reStructuredText format:

def calculate_area(length, width):
    """Calculate the area of a rectangle.

    :param length: The length of the rectangle.
    :type length: float
    :param width: The width of the rectangle.
    :type width: float
    :returns: The area of the rectangle.
    :rtype: float
    """
    return length * width

This format is very explicit and works seamlessly with Sphinx, a tool commonly used for generating professional documentation.

So which style should you choose? It often depends on your project or team preferences. The key is consistency—pick one style and stick with it throughout your codebase.

Now, let’s talk about what to include in your docstrings. A great docstring answers these questions:

  • What does this function/class/module do?
  • What parameters does it take, and what are their types?
  • What does it return?
  • What errors might it raise?

Here’s a more detailed example for a function that might raise an exception:

def divide_numbers(a, b):
    """Divide two numbers.

    Args:
        a (float): The numerator.
        b (float): The denominator.

    Returns:
        float: The result of a divided by b.

    Raises:
        ZeroDivisionError: If b is zero.
    """
    if b == 0:
        raise ZeroDivisionError("Cannot divide by zero!")
    return a / b

By including the Raises section, you’re giving users a heads-up about potential issues.

When writing docstrings for classes, you should describe the class’s purpose and document its methods. Here’s an example:

class Rectangle:
    """A class to represent a rectangle."""

    def __init__(self, length, width):
        """Initialize the rectangle with length and width.

        Args:
            length (float): The length of the rectangle.
            width (float): The width of the rectangle.
        """
        self.length = length
        self.width = width

    def area(self):
        """Calculate the area of the rectangle.

        Returns:
            float: The area of the rectangle.
        """
        return self.length * self.width

Notice that the class has a docstring, and each method has its own docstring too.

For modules, you can include docstrings at the top of the file to explain the module’s purpose, list key functions/classes, and provide any other relevant information. For example:

"""
This module provides utility functions for geometric calculations.

It includes functions for calculating areas of various shapes.
"""

# ... rest of the code ...

Another best practice is to be concise but thorough. Your docstring should be long enough to cover the essentials but avoid unnecessary verbosity. Also, write in the present tense and use active voice. For example, say “Calculates the area” rather than “This function will calculate the area.”

Let’s look at a comparison of docstring styles for the same function:

Style Parameter Format Return Format
Google param (type): desc type: desc
NumPy/SciPy param : type + indented type + indented
reStructuredText :param type param: desc :returns: desc

This table summarizes how parameters and returns are formatted in each major style.

You can access docstrings programmatically using __doc__ or the help() function. For example:

print(divide_numbers.__doc__)

Or:

help(divide_numbers)

This is incredibly useful for interactive sessions and when building tools that generate documentation.

Here are some things to avoid in your docstrings:

  • Don’t state the obvious. If the function name is get_name, you don’t need to write “Gets the name.”
  • Don’t include implementation details unless they’re critical for the user to know.
  • Avoid spelling and grammatical errors—they reduce clarity and professionalism.

If you’re working on a large project, consider using tools like Sphinx, pydoc, or Docutils to generate beautiful HTML documentation from your docstrings. Many teams also use linters like pydocstyle to ensure docstring consistency and completeness.

In summary, great docstrings make your code more maintainable, user-friendly, and professional. By following these best practices, you’ll not only help others understand your code but also reinforce your own understanding of what you’ve built.

Remember: code tells you how, docstrings tell you why. Happy documenting!