8 Practical Python Docstring Examples & Patterns

Emmanuel Mumba avatar
8 Practical Python Docstring Examples & Patterns

TL;DR: 8 Key Python Docstring Examples

  • Google Style: Highly readable and structured; great for auto-generating API docs. Best for large, collaborative projects.
  • NumPy/SciPy Style: Verbose and detailed; the standard for scientific and data-heavy libraries.
  • PEP 257/Sphinx: The foundational style using reStructuredText. Perfect for open-source projects needing comprehensive documentation websites.
  • Doctest Format: Embeds runnable tests directly in docstrings, guaranteeing examples are always correct.
  • RESTful API / OpenAPI: Describes HTTP endpoints and schemas; essential for web APIs and auto-generating interactive docs.
  • Minimal/Pragmatic: A simple one-liner for self-explanatory functions. Relies heavily on clear code and type hints.
  • Dataclass/Type-Hinted: Modern approach where type hints in the code handle type documentation, leaving the docstring to explain the why.
  • Epydoc/Javadoc Style: A legacy, tag-based format found in older codebases. Good to recognize but less common for new projects.

Table of Contents

Writing good documentation is non-negotiable for maintainable, scalable, and collaborative software projects. In Python, the docstring is the cornerstone of this practice. It’s not just a comment; it’s a feature of the language itself, accessible at runtime and essential for generating automated documentation.

However, “writing a docstring” isn’t a one-size-fits-all instruction. The Python ecosystem supports several competing styles, each with its own strengths. In my experience, choosing the right format or even understanding one you’ve inherited can be a significant hurdle. This is where a practical collection of python docstring examples becomes invaluable.

This guide provides clear, actionable examples for the most common docstring formats you’ll encounter. We’ll break down Google Style, NumPy/SciPy, Sphinx, and others, offering a practical analysis of when and why to use each one. You’ll learn not just what these docstrings look like, but how they function in real-world scenarios.

1. Google Style Docstrings

The Google Style docstring is a highly structured and readable format defined in the Google Python Style Guide. It’s renowned for its clarity, making it one of the most popular python docstring examples for projects that prioritize maintainability. This style enforces specific sections like Args, Returns, and Raises, which tools like Sphinx (with the Napoleon extension) can parse to auto-generate beautiful API documentation.

Example Breakdown

Let’s look at a function to fetch user data, documented using the Google style.

def fetch_user_data(user_id: int, include_profile: bool = True) -> dict:
    """Fetches user data from the database.

    This function queries the user database for a specific user ID
    and can optionally include extended profile information.

    Args:
        user_id (int): The unique identifier for the user.
        include_profile (bool): If True, includes the user's profile
            data in the response. Defaults to True.

    Returns:
        dict: A dictionary containing user data. Returns an empty
              dictionary if the user is not found.

    Raises:
        ValueError: If the user_id is not a positive integer.
    """
    if not isinstance(user_id, int) or user_id <= 0:
        raise ValueError("User ID must be a positive integer.")
    # ... database fetching logic here ...
    user_data = {"id": user_id, "name": "Jane Doe"}
    if include_profile:
        user_data["profile"] = {"email": "[email protected]"}
    return user_data

Strategic Analysis & Actionable Takeaways

  • Clarity Through Structure: The Args, Returns, and Raises sections create a predictable and scannable format. Developers immediately know where to look.

  • Type Hinting Integration: The style seamlessly combines docstring type declarations (user_id (int):) with modern Python type hints (user_id: int).

  • Descriptive Parameter Details: Each argument includes its name, type, and a concise description. For optional parameters, clearly state the default behavior (Defaults to True).

Key Takeaway: Adopt the Google style when your priority is generating clean, automated API documentation and enforcing a strict, consistent format across a large team. Its structure minimizes ambiguity and is ideal for complex functions and APIs.

2. NumPy/SciPy Style Docstrings

The NumPy/SciPy style docstring is a comprehensive format born from the needs of the scientific computing community. It’s meticulously detailed, making it the standard for projects like NumPy, SciPy, and Pandas. This format is designed to clearly document complex functions with numerous parameters. Like Google style, it’s supported by Sphinx, making it an excellent choice among python docstring examples for technical documentation.

NumPy/SciPy Style Docstrings

Caption: The NumPy docstring format uses clear, underlined sections to organize complex information about parameters, return values, and related functions.

Example Breakdown

Let’s examine a function that calculates the dot product of two vectors, documented in the NumPy style.

import numpy as np

def vector_dot_product(x: np.ndarray, y: np.ndarray) -> float:
    """Calculate the dot product of two vectors.

    This function takes two 1-D arrays of the same size and computes
    their dot product.

    Parameters
    ----------
    x : np.ndarray
        First input vector. A 1-dimensional array of numbers.
    y : np.ndarray
        Second input vector. Must have the same shape as `x`.

    Returns
    -------
    float
        The scalar dot product of the input vectors.

    Raises
    ------
    ValueError
        If the input arrays are not 1-dimensional or have mismatched shapes.

    See Also
    --------
    numpy.dot : Equivalent function in the NumPy library.
    """
    if x.ndim != 1 or y.ndim != 1:
        raise ValueError("Input arrays must be 1-dimensional.")
    if x.shape != y.shape:
        raise ValueError("Input arrays must have the same shape.")
    return np.dot(x, y)

Strategic Analysis & Actionable Takeaways

  • Explicit Section Headers: The NumPy style uses underlined headers (Parameters, Returns, Raises). This visual separation makes dense docstrings highly readable.

  • Detailed Parameter Descriptions: Each parameter is defined with its name, type, and description on separate lines, allowing for more extensive explanations.

  • Contextual Cross-Referencing: The See Also section is a powerful feature for building an interconnected documentation system, guiding users to related functions.


Key Takeaway: Use the NumPy/SciPy style for technical libraries where functions have complex parameters, especially arrays. Its structured, verbose format is perfect for documenting mathematical details and data shapes.

3. PEP 257 / Sphinx-Compatible Docstrings

PEP 257 is the foundational document defining the conventions for good docstrings in Python. It’s the community’s official standard, emphasizing simplicity and consistency. Docstrings following this convention are designed to be parsed by automated tools, most notably Sphinx, to generate professional documentation. This style serves as the base for many other formats, making it one of the most fundamental python docstring examples to master.

Example Breakdown

Let’s examine a class documentation that adheres to PEP 257 principles and is formatted with reStructuredText (reST) for Sphinx.

class APIClient:
    """A client to interact with a remote API.

    This class provides a high-level interface for making HTTP requests
    to a specified API endpoint. It handles authentication and
    session management automatically.

    :param base_url: The base URL for the API endpoint.
    :type base_url: str
    :param api_key: The API key for authentication.
    :type api_key: str
    """

    def __init__(self, base_url: str, api_key: str):
        self.base_url = base_url
        self._api_key = api_key
        # ... session initialization logic ...

    def get_resource(self, resource_id: int) -> dict:
        """Retrieve a specific resource by its ID.

        :param resource_id: The ID of the resource to fetch.
        :type resource_id: int
        :return: A dictionary representing the resource.
        :rtype: dict
        :raises ConnectionError: If the API is unreachable.
        """
        # ... request logic here ...
        if resource_id <= 0:
            raise ValueError("Resource ID must be positive.")
        return {"id": resource_id, "data": "sample data"}

Strategic Analysis & Actionable Takeaways

  • Foundation of All Styles: PEP 257 establishes the core principles: a one-line summary followed by a more descriptive paragraph. Understanding this is crucial.

  • reStructuredText (reST) Integration: This style relies on reST directives like :param:, :type:, :return:, and :raises:. While verbose, this format is powerful, directly feeding into the Sphinx documentation engine.

  • Explicit Type Declaration: This style often separates parameter name and type into :param: and :type: tags. This explicitness ensures maximum compatibility with documentation tools.


Key Takeaway: Adopt the PEP 257/Sphinx style when building libraries or open-source projects where generating comprehensive, cross-referenced documentation is a primary goal. To dive deeper, you can learn more about function documentation in Python and its core principles.

4. Doctest Format Docstrings

The Doctest format is a unique approach that embeds executable test cases directly within your docstrings. As one of the most practical python docstring examples, it serves a dual purpose: it documents the code’s behavior with concrete examples and simultaneously acts as a verifiable test suite. Python’s built-in doctest module can discover and run these examples, ensuring your documentation never becomes outdated.

Doctest Format Docstrings

Example Breakdown

Let’s examine a simple function for calculating an average, documented with integrated doctests.

import doctest

def calculate_average(numbers: list[float]) -> float:
    """Calculates the average of a list of numbers.

    This function returns the arithmetic mean of a list containing
    numeric values. It handles empty lists by returning 0.0.

    Examples:
        >>> calculate_average([1, 2, 3, 4, 5])
        3.0
        >>> calculate_average([10.5, 19.5])
        15.0
        >>> calculate_average([])
        0.0
        >>> calculate_average([-1, 1])
        0.0
    """
    if not numbers:
        return 0.0
    return sum(numbers) / len(numbers)

if __name__ == "__main__":
    doctest.testmod() # Runs the tests embedded in the docstring

Strategic Analysis & Actionable Takeaways

  • Living Documentation: The primary advantage is that your examples are guaranteed to be correct. If the code changes and breaks an example, the doctest will fail.

  • Clarity Through Demonstration: Doctests show users exactly how to use the function with real inputs and expected outputs. This is often more intuitive than text alone.

  • Simplified Testing: For simple functions, doctests provide a low-friction way to add test coverage without the overhead of a separate test file.

Key Takeaway: Use the Doctest format when you want to provide clear, executable examples that double as a basic test suite. It is ideal for utility libraries and functions whose behavior is best explained by demonstration.

5. RESTful API / OpenAPI Docstrings

When documenting web services, standard docstrings fall short. RESTful API docstrings, structured according to the OpenAPI Specification (formerly Swagger), are designed for this purpose. They describe HTTP endpoints, request/response models, and status codes. This format is a prime example of how python docstring examples can power entire developer ecosystems.

RESTful API / OpenAPI Docstrings

Example Breakdown

Modern Python web frameworks like FastAPI integrate OpenAPI directly into the docstring. This allows developers to write documentation right alongside their endpoint logic.

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
    name: str
    price: float

@app.post("/items/", status_code=201)
def create_item(item: Item):
    """
    Creates a new item.

    This endpoint adds a new item to the inventory based on the
    provided data. It performs validation and returns the created
    item's data.

    - **name**: The name of the item.
    - **price**: The selling price of the item.
    """
    # ... logic to save the item to a database ...
    return {"message": "Item created successfully", "item_data": item}

Strategic Analysis & Actionable Takeaways

  • Machine-Readable by Design: The primary goal is machine-readability. Tools like Swagger UI and ReDoc consume the generated OpenAPI schema to create interactive documentation.

  • Framework Integration: Frameworks like FastAPI use these docstrings to automatically build the API schema. The docstring’s summary becomes the endpoint’s description.

  • Single Source of Truth: This approach makes the code the single source of truth for the API’s contract. When the code changes, a simple docstring update ensures the public documentation is also updated. You can explore a variety of these implementations with these OpenAPI examples.


Key Takeaway: Use the OpenAPI docstring pattern when building any web API. It transforms documentation from a static, manual task into a dynamic, automated process.

6. Epydoc / Javadoc-Style Docstrings

The Epydoc style is a format inspired by Java’s Javadoc comment system. It uses @ tags like @param and @return to explicitly label different parts of the docstring. While less common in new Python projects, which often prefer Google or NumPy styles, its influence is still seen in some legacy codebases.

Example Breakdown

Let’s adapt our function to use the Epydoc style.

def fetch_user_data(user_id, include_profile=True):
    """Fetches user data from the database.

    This function queries the user database for a specific user ID
    and can optionally include extended profile information.

    @param user_id: The unique identifier for the user.
    @type user_id: int
    @param include_profile: If True, includes the user's profile
                            data in the response.
    @type include_profile: bool
    @return: A dictionary containing user data. Returns an empty
             dictionary if the user is not found.
    @rtype: dict
    @raise ValueError: If the user_id is not a positive integer.
    """
    if not isinstance(user_id, int) or user_id <= 0:
        raise ValueError("User ID must be a positive integer.")
    # ... database fetching logic here ...
    user_data = {"id": user_id, "name": "Jane Doe"}
    if include_profile:
        user_data["profile"] = {"email": "[email protected]"}
    return user_data

Strategic Analysis & Actionable Takeaways

  • Explicit Tagging: The use of @ tags like @param, @type, and @return leaves no ambiguity. Each piece of metadata is clearly marked.

  • Separation of Concerns: This style strictly separates the parameter’s name (@param) from its type (@type), making it verbose but granular.

  • Legacy System Compatibility: Its primary relevance today is in maintaining older codebases. Understanding this style is crucial for working on long-running enterprise projects.

Key Takeaway: Use the Epydoc style only when you must maintain consistency within a legacy project. For new projects, choose a more modern format like Google or NumPy style.

7. Minimal/Pragmatic Docstrings

Not all functions require an exhaustive docstring. The minimal, or pragmatic, approach champions brevity, advocating for documentation only where it adds real value. This style is perfect for simple, self-explanatory functions where clear naming and type hints convey most of the necessary information.

Example Breakdown

Consider a simple helper function to format a user’s full name. Its purpose is clear from its name and type hints, making a lengthy docstring redundant.

def format_full_name(first_name: str, last_name: str) -> str:
    """Joins the first and last name into a single string."""
    if not first_name or not last_name:
        return ""
    return f"{first_name.strip().title()} {last_name.strip().title()}"

Strategic Analysis & Actionable Takeaways

  • Clarity Over Verbosity: The one-line summary is sufficient. It confirms the function’s purpose without restating the obvious.

  • Reliance on Code Readability: A minimal docstring is only effective when the function name (format_full_name) and type hints (str) are exceptionally clear.

  • Reduced Maintenance: For simple utilities, overly detailed docstrings become a liability. This lean approach ensures documentation is less likely to become stale. You can learn more about keeping documentation in sync with your code in this guide on function documentation.

Key Takeaway: Employ minimal docstrings for internal helper functions or simple, self-evident code. This pragmatic approach depends on writing highly readable code with descriptive names and strong type hinting.

8. Dataclass / Type-Hinted Docstrings

Modern Python (3.6+) encourages using type hints directly in the function signature, as defined in PEP 484. This shifts the responsibility of documenting types from the docstring to the code itself, leading to cleaner documentation. This is one of the most practical python docstring examples for projects leveraging frameworks like FastAPI or Pydantic. The docstring can then focus on explaining the why and how.

Example Breakdown

Let’s look at a function that processes a user profile defined using a Pydantic BaseModel. The docstring is minimal because the type hints are self-documenting.

from typing import Optional
from pydantic import BaseModel, Field

class UserProfile(BaseModel):
    """Represents a user's profile data model."""
    user_id: int = Field(..., description="The unique user identifier.")
    username: str = Field(..., description="The user's public username.")
    bio: Optional[str] = None

def update_user_bio(profile: UserProfile, new_bio: str) -> UserProfile:
    """Updates the biography for a given user profile.

    This function takes a user profile object and sets a new bio,
    returning the modified profile. It relies on the UserProfile model
    for data validation.

    Args:
        profile (UserProfile): The user profile object to update.
        new_bio (str): The new biography text for the user.

    Returns:
        UserProfile: The updated user profile object with the new bio.
    """
    profile.bio = new_bio
    return profile

Strategic Analysis & Actionable Takeaways

  • Single Source of Truth: Type information (UserProfile, str) resides in the function signature, eliminating drift between code and docs.

  • Reduced Docstring Boilerplate: Since types are explicit in the code, the docstring can be lighter. The Args section explains the purpose of each parameter, not its type.

  • IDE and Linter Integration: This style is highly compatible with modern IDEs, linters, and static analysis tools like Mypy, which improves developer productivity.

Key Takeaway: Adopt this type-hint-first approach in a modern Python environment, especially with libraries that use type hints for validation or serialization. It creates more maintainable and less redundant documentation.

Comparison of 8 Python Docstring Styles

StyleπŸ”„ Implementation Complexity⚑ Resource / Tooling⭐ Expected OutcomesπŸ“Š Ideal Use CasesπŸ’‘ Key Advantages / Tips
Google Style DocstringsπŸ”„ Medium β€” structured sections, needs consistency⚑ Sphinx/Doc generators; moderate maintenance⭐⭐⭐⭐ β€” Clear, scannable API docsLarge projects, team APIs, public librariesπŸ’‘ Include Args/Returns, use concrete types and examples
NumPy/SciPy Style DocstringsπŸ”„ High β€” verbose, detailed shape/type specs⚑ Sphinx (numpydoc), extensive examples; higher authoring cost⭐⭐⭐⭐⭐ β€” Precise for numerical APIs and arraysScientific computing, numerical libraries, research codeπŸ’‘ Document array shapes/dtypes and provide multiple examples
PEP 257 / Sphinx-CompatibleπŸ”„ Low β€” minimal, consistent convention⚑ Sphinx, doctest support; low overhead⭐⭐⭐ β€” Clean, general-purpose and standards-compliantStandard Python libs, general open-source projectsπŸ’‘ Start with one-line summary; use reST and runnable doctests
Doctest Format DocstringsπŸ”„ Medium β€” embed executable examples, maintain outputs⚑ Python doctest, pytest –doctest-modules; CI recommended⭐⭐⭐ β€” Docs double as tests; catches drift earlyTutorials, simple functions, test-driven docsπŸ’‘ Keep examples simple, use ellipsis for long output, run in CI
RESTful API / OpenAPI DocstringsπŸ”„ High β€” spec-driven, endpoint and schema detail⚑ OpenAPI/Swagger, FastAPI/ReDoc; schema validation tools⭐⭐⭐⭐ β€” Interactive docs and clear API contractsWeb APIs, microservices, public endpointsπŸ’‘ Document status codes and example bodies; use decorators
Epydoc / Javadoc-Style DocstringsπŸ”„ Medium β€” tag-based, can be verbose/outdated⚑ Epydoc (legacy); limited modern tool support⭐⭐ β€” Strong metadata but less Pythonic and declining supportLegacy codebases, Java-to-Python migrationsπŸ’‘ Prefer migrating to Google/NumPy styles; add type hints
Minimal / Pragmatic DocstringsπŸ”„ Low β€” very brief, low maintenance⚑ Rely on clear names and PEP 484 type hints; minimal tooling⭐⭐⭐ β€” Fast to write, low drift for simple codeInternal helpers, prototypes, simple utilitiesπŸ’‘ Document “why” not “what”; combine with type hints
Dataclass / Type-Hinted DocstringsπŸ”„ Low–Medium β€” leverage code-level types, less doc text⚑ mypy, Pydantic, IDEs (PyCharm/VS Code); static type checkers⭐⭐⭐⭐ β€” Strong IDE/autocomplete and type safetyModern Python projects, APIs with validation, data pipelinesπŸ’‘ Use Optional/Union/TypedDict for clarity; keep docstrings concise

Final Thoughts

We’ve explored a wide spectrum of python docstring examples, from the structured verbosity of Google and NumPy styles to the lean, pragmatic approach of minimal docstrings. Each style serves a unique purpose. The key isn’t to find a single “best” style, but to understand the trade-offs and choose the one that aligns with your project’s goals.

A great docstring is more than just a comment; it’s a contract. It tells other developers what a piece of code promises to do, what it expects as input, and what it will deliver as output. This clarity is the foundation of maintainable software development.

Your Actionable Takeaways

As you move forward, keep these core principles in mind:

  • Consistency is King: Choose a style and stick with it. Inconsistent documentation is often more confusing than no documentation at all.
  • The “Why” Matters Most: Don’t just explain what the code does; explain why it exists. What problem does this function solve?
  • Docstrings are for Humans and Machines: They must be readable for developers and parsable for tools that generate documentation sites.
  • Evolve Your Documentation: Documentation is not a one-time task. An outdated docstring is a trap. Make documentation updates a required part of your pull request process.

Mastering the art of writing effective docstrings is a significant step toward becoming a more proficient developer. It’s a practice that pays dividends in reduced onboarding time, fewer bugs, and a more resilient codebase.

Keeping docstrings accurate and in sync with your code is a continuous challenge, especially in fast-moving projects. If you find your team struggling with documentation drift, consider automating the process. DeepDocs is a GitHub-native AI app that keeps your documentation up to date by automatically detecting code changes and updating the relevant docs, ensuring they never fall out of sync again.

Leave a Reply

Discover more from DeepDocs

Subscribe now to keep reading and get access to the full archive.

Continue reading