Comments and Documentation in Python: Best Practices

Comments and documentation are essential for writing maintainable Python code, serving as communication tools that explain intent, clarify complex logic, and provide usage instructions for functions, classes, and modules. Python supports inline comments using hash symbols for brief explanations, multi-line docstrings for comprehensive documentation accessible through help systems, and follows PEP 8 style guidelines for comment formatting plus PEP 257 conventions for docstring structure. Proper documentation distinguishes professional code from amateur projects, enabling team collaboration, reducing onboarding time for new developers, facilitating code reviews, and ensuring long-term maintainability as projects evolve and original authors move to other responsibilities.
This comprehensive guide explores inline comments following PEP 8 guidelines for placement and style, docstrings conforming to PEP 257 conventions for functions, classes, and modules, popular docstring formats including Google, NumPy, and Sphinx styles, type hints complementing documentation with static type information, documentation generation tools like Sphinx and MkDocs, common documentation mistakes to avoid, and best practices for writing clear, maintainable documentation. Whether you're building open-source libraries requiring public API documentation, enterprise applications needing comprehensive internal documentation, or personal projects benefiting from self-documenting code, mastering Python's commenting and documentation techniques ensures your code remains understandable, maintainable, and professional throughout its lifecycle.
Inline Comments and PEP 8 Guidelines
Inline comments use hash symbols to add explanations within code, helping clarify non-obvious logic, document workarounds, explain complex algorithms, and provide context for future maintainers. PEP 8 establishes guidelines including placing comments on separate lines above code rather than trailing, using two spaces before inline comments when necessary, starting with a capital letter and space after the hash, and keeping comments concise while avoiding stating the obvious. Good comments explain why code exists rather than what it does, since well-written Python should be self-documenting through clear variable names and logical structure.
# Inline Comments Examples
# Good: Comment on separate line above code
# Calculate compound interest using the standard formula
total = principal * (1 + rate) ** years
# Bad: Obvious comment stating what code does
# Increment x by 1
x = x + 1
# Good: Explain why, not what
# Use exponential backoff to avoid overwhelming the API
time.sleep(2 ** retry_count)
# Inline comments with two spaces
x = x + 1 # Compensate for border offset
# Good: Explain non-obvious logic
# Fast inverse square root algorithm from Quake III
threehalfs = 1.5
x2 = number * 0.5
i = struct.unpack('>l', struct.pack('>f', number))[0]
i = 0x5f3759df - (i >> 1)
y = struct.unpack('>f', struct.pack('>l', i))[0]
y = y * (threehalfs - (x2 * y * y))
# Good: Document workarounds
# TODO: Remove this hack when API v2 is available
if response.status_code == 429:
time.sleep(60)
# Good: Explain business logic
# Senior citizens (65+) get 20% discount
if age >= 65:
discount = 0.20
# Bad: Redundant comment
# Loop through users
for user in users:
process_user(user)
# Good: Warning about edge cases
# Note: Returns None if database connection fails
result = fetch_data()
# Block comments for complex sections
# The following algorithm implements Dijkstra's shortest path
# algorithm for finding optimal routes in a weighted graph.
# Time complexity: O((V + E) log V)
# Space complexity: O(V)
def dijkstra(graph, start):
# Implementation here
passDocstrings: PEP 257 Conventions
Docstrings are string literals appearing as the first statement in modules, functions, classes, and methods, providing documentation accessible through help() and __doc__ attributes. PEP 257 establishes conventions including using triple double quotes even for one-liners, placing closing quotes on the same line for single-line docstrings, writing imperative mood summaries ("Return" not "Returns"), and structuring multi-line docstrings with summary line, blank line, then detailed description. Docstrings serve as live documentation that stays synchronized with code, unlike external documents that often become outdated.
Basic Docstring Structure
# Basic Docstring Examples
# One-line docstring
def square(n):
"""Return the square of n."""
return n ** 2
# Multi-line docstring
def calculate_interest(principal, rate, years):
"""Calculate compound interest.
This function computes the total amount after applying
compound interest to a principal sum over a specified
number of years.
Args:
principal: Initial investment amount
rate: Annual interest rate (as decimal)
years: Number of years for investment
Returns:
Total amount after compound interest
Example:
>>> calculate_interest(1000, 0.05, 10)
1628.89
"""
return principal * (1 + rate) ** years
# Class docstring
class BankAccount:
"""Represent a bank account with basic operations.
This class provides methods for depositing, withdrawing,
and checking balance. All amounts are in USD.
Attributes:
account_number: Unique identifier for account
balance: Current account balance
owner: Account holder name
"""
def __init__(self, account_number, owner):
"""Initialize a new bank account.
Args:
account_number: Unique account identifier
owner: Name of account holder
"""
self.account_number = account_number
self.owner = owner
self.balance = 0.0
def deposit(self, amount):
"""Add funds to account.
Args:
amount: Amount to deposit (must be positive)
Raises:
ValueError: If amount is negative
"""
if amount < 0:
raise ValueError("Amount must be positive")
self.balance += amount
# Module docstring (at top of file)
"""Banking utilities for account management.
This module provides classes and functions for managing
bank accounts, transactions, and interest calculations.
Example:
from banking import BankAccount
account = BankAccount("12345", "Alice")
account.deposit(1000)
"""Popular Docstring Formats
While PEP 257 defines general docstring conventions, several popular formatting styles provide structured templates for documenting parameters, return values, and exceptions. Google style emphasizes readability with clear section headers, NumPy/SciPy style uses underlined sections popular in scientific computing, and Sphinx/reStructuredText style integrates with documentation generation tools. Choosing and consistently applying one style throughout a project ensures uniform documentation that automated tools can parse for generating reference documentation.
Google Style Docstrings
# Google Style Docstrings
def fetch_user_data(user_id, include_posts=False, timeout=30):
"""Fetch user data from the API.
Retrieves comprehensive user information including profile details,
settings, and optionally their posts. Handles rate limiting and
connection errors gracefully.
Args:
user_id (int): Unique identifier for the user
include_posts (bool): Whether to include user posts. Defaults to False.
timeout (int): Request timeout in seconds. Defaults to 30.
Returns:
dict: User data containing:
- name (str): User's full name
- email (str): User's email address
- posts (list): List of posts if include_posts is True
Raises:
ValueError: If user_id is negative
APIError: If API request fails
TimeoutError: If request exceeds timeout
Example:
>>> user = fetch_user_data(123, include_posts=True)
>>> print(user['name'])
'Alice Smith'
Note:
This function implements exponential backoff for rate limiting.
Maximum 3 retry attempts with increasing delays.
"""
if user_id < 0:
raise ValueError("User ID must be positive")
# Implementation here
pass
class DataProcessor:
"""Process and transform data from various sources.
This class provides methods for loading, cleaning, and transforming
data from different formats including CSV, JSON, and databases.
Attributes:
source_type (str): Type of data source ('csv', 'json', 'db')
config (dict): Configuration options for processing
cache (dict): Internal cache for processed data
Example:
>>> processor = DataProcessor('csv')
>>> data = processor.load('data.csv')
>>> cleaned = processor.clean(data)
"""
def __init__(self, source_type):
"""Initialize the data processor.
Args:
source_type (str): Type of data source
Raises:
ValueError: If source_type is not supported
"""
self.source_type = source_type
self.config = {}
self.cache = {}NumPy Style Docstrings
# NumPy Style Docstrings
def calculate_statistics(data, method='mean', axis=0):
"""Calculate statistical measures for dataset.
Computes various statistical measures including mean, median,
and standard deviation along specified axis.
Parameters
----------
data : array_like
Input data array or list of numbers
method : {'mean', 'median', 'std'}, optional
Statistical method to apply. Default is 'mean'.
axis : int, optional
Axis along which to compute statistic. Default is 0.
Returns
-------
float or ndarray
Computed statistical value. Returns float for 1D data,
ndarray for multi-dimensional data.
Raises
------
ValueError
If method is not recognized
TypeError
If data is not numeric
See Also
--------
numpy.mean : Compute arithmetic mean
numpy.median : Compute median value
Notes
-----
This function handles missing values by ignoring them in
calculations. For data with many missing values, consider
using specialized imputation methods first.
The algorithm uses Welford's online algorithm for numerical
stability when computing variance and standard deviation.
Examples
--------
>>> data = [1, 2, 3, 4, 5]
>>> calculate_statistics(data, method='mean')
3.0
>>> data = [[1, 2], [3, 4]]
>>> calculate_statistics(data, method='mean', axis=0)
array([2., 3.])
References
----------
.. [1] Welford, B.P. (1962). "Note on a method for calculating
corrected sums of squares and products". Technometrics.
"""
# Implementation here
passType Hints and Documentation
Type hints introduced in Python 3.5+ complement docstrings by providing machine-readable type information for parameters and return values, enabling static type checking with tools like mypy, improving IDE autocomplete, and serving as inline documentation. While type hints don't replace docstrings, they reduce documentation burden by making types explicit, allowing docstrings to focus on explaining behavior, constraints, and examples. Combining type hints with well-written docstrings creates comprehensive documentation that serves both human readers and automated tools.
# Type Hints with Documentation
from typing import List, Dict, Optional, Union
def process_users(
users: List[Dict[str, str]],
filter_active: bool = True
) -> List[str]:
"""Extract names from user records.
Processes a list of user dictionaries and extracts their names,
optionally filtering for active users only.
Args:
users: List of user dictionaries with 'name' and 'active' keys
filter_active: Whether to include only active users
Returns:
List of user names as strings
Example:
>>> users = [{'name': 'Alice', 'active': True}]
>>> process_users(users)
['Alice']
"""
result = []
for user in users:
if not filter_active or user.get('active', False):
result.append(user['name'])
return result
def find_user(user_id: int) -> Optional[Dict[str, Union[str, int]]]:
"""Find user by ID.
Searches the database for a user with the specified ID.
Args:
user_id: Unique identifier for the user
Returns:
User dictionary if found, None otherwise. Dictionary contains:
- name: User's full name (str)
- age: User's age (int)
- email: User's email (str)
Example:
>>> user = find_user(123)
>>> if user:
... print(user['name'])
"""
# Implementation here
pass
class User:
"""Represent a user account.
Attributes:
user_id: Unique identifier
username: Login username
email: Contact email address
"""
def __init__(
self,
user_id: int,
username: str,
email: str
) -> None:
"""Initialize a new user.
Args:
user_id: Unique identifier for user
username: Desired username for login
email: User's email address
"""
self.user_id = user_id
self.username = username
self.email = email
def send_email(self, subject: str, body: str) -> bool:
"""Send email to user.
Args:
subject: Email subject line
body: Email body content
Returns:
True if email sent successfully, False otherwise
"""
# Implementation here
return TrueDocumentation Generation Tools
Documentation generation tools automatically create HTML, PDF, or other format documentation from docstrings and code structure, saving time and ensuring consistency. Sphinx is the most popular Python documentation tool supporting multiple docstring styles and extensions, MkDocs provides a simpler Markdown-based alternative, and pdoc generates API documentation automatically. These tools parse docstrings, organize content, create navigation, and produce professional documentation websites from your code, making comprehensive documentation accessible to users without manual HTML creation.
# Documentation Tool Examples
# Sphinx-style docstring (reStructuredText)
def process_data(data, validate=True):
"""Process input data with optional validation.
:param data: Input data to process
:type data: list
:param validate: Whether to validate data before processing
:type validate: bool
:return: Processed data results
:rtype: dict
:raises ValueError: If validation fails
.. note::
This function modifies data in-place for efficiency.
.. warning::
Ensure data is sanitized before processing.
Example::
>>> result = process_data([1, 2, 3])
>>> print(result['status'])
'success'
"""
pass
# Module-level documentation for Sphinx
"""Data Processing Module
========================
This module provides utilities for processing and validating
various data formats.
Available Functions
-------------------
* :func:`process_data` - Process input data
* :func:`validate_data` - Validate data structure
* :func:`transform_data` - Transform data format
Usage Example
-------------
.. code-block:: python
from data_processor import process_data
result = process_data([1, 2, 3], validate=True)
print(result)
See Also
--------
* :mod:`data_validator` - Additional validation utilities
* :mod:`data_transformer` - Transformation functions
"""
# Sphinx configuration example (conf.py)
# project = 'My Project'
# extensions = [
# 'sphinx.ext.autodoc',
# 'sphinx.ext.napoleon', # Google/NumPy style
# 'sphinx.ext.viewcode',
# ]
# napoleon_google_docstring = True
# napoleon_numpy_docstring = True
# MkDocs example (mkdocs.yml)
# site_name: My Project
# theme:
# name: material
# plugins:
# - search
# - mkdocstrings:
# handlers:
# python:
# options:
# docstring_style: googleDocumentation Best Practices
Effective documentation requires following established conventions, maintaining consistency, updating docs with code changes, and focusing on clarity over cleverness. Write for your audience whether they're end users needing usage examples or developers requiring implementation details, keep documentation synchronized with code through review processes, and avoid common pitfalls like outdated comments, obvious statements, and missing edge case explanations.
- Write docstrings for all public APIs: Every module, class, and function intended for external use needs a docstring explaining purpose, parameters, and return values
- Update docs with code: Treat documentation updates as part of code changes. Outdated documentation is worse than no documentation as it misleads users
- Include examples: Add usage examples in docstrings showing common use cases. Examples clarify intent better than lengthy descriptions
- Document exceptions: List all exceptions functions can raise and conditions triggering them. This prevents unexpected crashes and improves error handling
- Use consistent style: Choose one docstring format (Google, NumPy, or Sphinx) and apply it throughout your project for uniformity
- Avoid obvious comments: Don't document what code clearly shows. Comment why decisions were made, not what operations perform
- Document edge cases: Explain behavior with empty inputs, None values, boundary conditions, and error scenarios users might encounter
- Keep docs concise: Write clear, brief documentation. Long-winded explanations discourage reading. Be thorough but economical with words
- Use TODO and FIXME tags: Mark incomplete code with TODO and known issues with FIXME so they're easily searchable and don't get forgotten
- Review documentation: Include docstring reviews in code review process. Fresh eyes catch unclear explanations and missing information
Common Documentation Mistakes
# Common Documentation Mistakes
# MISTAKE 1: Outdated documentation
def calculate_total(items, tax_rate):
"""Calculate total with 5% tax."""
# Documentation says 5% but parameter is tax_rate!
return sum(items) * (1 + tax_rate)
# CORRECT
def calculate_total(items, tax_rate):
"""Calculate total price including tax.
Args:
items: List of item prices
tax_rate: Tax rate as decimal (e.g., 0.05 for 5%)
Returns:
Total price including tax
"""
return sum(items) * (1 + tax_rate)
# MISTAKE 2: Obvious comments
def increment(x):
# Add 1 to x
x = x + 1
# Return x
return x
# CORRECT: Self-documenting code, no comments needed
def increment(x):
return x + 1
# MISTAKE 3: Missing exception documentation
def divide(a, b):
"""Divide a by b."""
return a / b # Can raise ZeroDivisionError!
# CORRECT
def divide(a, b):
"""Divide a by b.
Args:
a: Numerator
b: Denominator
Returns:
Result of division
Raises:
ZeroDivisionError: If b is zero
"""
return a / b
# MISTAKE 4: Vague descriptions
def process(data):
"""Process data."""
pass
# CORRECT
def process(data):
"""Transform raw sensor data into normalized format.
Removes outliers, fills missing values, and scales to 0-1 range.
Args:
data: Raw sensor readings as list of floats
Returns:
Normalized data array
"""
pass
# MISTAKE 5: No examples for complex functions
def parse_config(config_string):
"""Parse configuration string."""
pass
# CORRECT
def parse_config(config_string):
"""Parse configuration string into dictionary.
Args:
config_string: Configuration in 'key=value' format,
separated by semicolons
Returns:
Dictionary of configuration key-value pairs
Example:
>>> config = parse_config('db=localhost;port=5432')
>>> config['db']
'localhost'
"""
passConclusion
Effective comments and documentation distinguish professional Python code from amateur projects, serving as essential communication tools that explain intent, clarify complex logic, and provide usage instructions throughout the software lifecycle. Inline comments following PEP 8 guidelines use hash symbols for brief explanations, appearing on separate lines above code rather than trailing, explaining why code exists rather than what it does, and avoiding obvious statements that well-written code makes clear through expressive variable names and logical structure. Docstrings conforming to PEP 257 conventions use triple double quotes appearing as first statements in modules, functions, classes, and methods, providing documentation accessible through help systems with one-line summaries for simple cases and multi-line structure including blank lines, detailed descriptions, parameter documentation, return value explanations, and exception listings for complex functions.
Popular docstring formats including Google style emphasizing readability, NumPy style with underlined sections standard in scientific computing, and Sphinx style integrating with documentation generators provide structured templates ensuring consistency throughout projects. Type hints introduced in Python 3.5+ complement docstrings with machine-readable type information enabling static analysis tools, improving IDE support, and reducing documentation burden by making types explicit while allowing docstrings to focus on behavior, constraints, and examples. Documentation generation tools like Sphinx, MkDocs, and pdoc automatically create professional documentation websites from docstrings, parsing multiple styles, organizing content with navigation, and producing HTML or PDF output without manual formatting. Best practices emphasize writing docstrings for all public APIs, updating documentation with code changes, including usage examples, documenting exceptions and edge cases, maintaining consistent style, avoiding obvious comments, keeping documentation concise, using TODO and FIXME tags for tracking, and reviewing documentation during code reviews. Common mistakes include outdated documentation contradicting code, obvious comments stating what code shows, missing exception documentation, vague descriptions lacking specificity, and complex functions without examples demonstrating usage. By mastering inline comments following PEP 8 guidelines, docstrings adhering to PEP 257 conventions, popular formatting styles, type hints, documentation tools, and best practices while avoiding common pitfalls, you ensure your Python code remains understandable, maintainable, and professional, facilitating team collaboration, reducing onboarding time, improving code reviews, and supporting long-term evolution as projects grow and original authors transition to new responsibilities.
$ share --platform
$ cat /comments/ (0)
$ cat /comments/
// No comments found. Be the first!


