Comments in Python — Best Practices for Readable Code

Sanjeev SharmaSanjeev Sharma
5 min read

Advertisement

Introduction

Why This Matters

Comments are the silent communicators of your codebase. They explain the why behind your code, document edge cases, and help future developers (including your future self) understand intent without re-reading every line. In Python, good commenting practices are especially important because the language's readability philosophy — "code should read like English" — means comments must add value, not noise.

The Python community, through PEP 8 and PEP 257, has established clear conventions for comments and docstrings. Following these conventions is essential when contributing to open-source projects, working in teams, or building libraries that others will import.

Poor commenting habits are among the most common code review feedback items. Too few comments leave readers guessing; too many become clutter that goes stale as code evolves. Learning the balance is a key professional skill.

Single-Line Comments

Single-line comments begin with # and extend to the end of the line. They should be placed above the code they describe, not inline unless the note is very brief.

# Calculate the area of a circle
area = 3.14159 * radius ** 2
 
# Avoid: inline comments that state the obvious
x = x + 1  # increment x by 1  <- redundant, adds no value
 
# Good: inline comment explaining why
x = x + 1  # offset by 1 to match 1-indexed API response

Multi-Line Comments

Python does not have a dedicated multi-line comment syntax. Developers use consecutive # lines or triple-quoted strings (though the latter is technically a string, not a comment).

# This function implements the Fisher-Yates shuffle algorithm.
# It runs in O(n) time and modifies the list in place.
# Do not use random.shuffle() here as it doesn't support
# reproducible seeds in this context.
def shuffle(items: list, seed: int) -> None:
    import random
    rng = random.Random(seed)
    for i in range(len(items) - 1, 0, -1):
        j = rng.randint(0, i)
        items[i], items[j] = items[j], items[i]

Docstrings

Docstrings (PEP 257) are triple-quoted strings immediately following a function, class, or module definition. They become the __doc__ attribute and are shown by help() and IDEs.

def calculate_discount(price: float, rate: float) -> float:
    """
    Calculate the discounted price.
 
    Args:
        price: The original price in USD.
        rate: Discount rate as a decimal (e.g., 0.2 for 20%).
 
    Returns:
        The price after applying the discount.
 
    Raises:
        ValueError: If rate is not between 0 and 1.
 
    Example:
        >>> calculate_discount(100.0, 0.2)
        80.0
    """
    if not (0 &lt;= rate &lt;= 1):
        raise ValueError(f"Rate must be between 0 and 1, got {rate}")
    return price * (1 - rate)

Class and Module Docstrings

"""
payment_utils.py — Utilities for processing payments.
 
This module provides helper functions for discount calculation,
tax application, and currency conversion. It wraps the internal
billing API and normalizes responses.
"""
 
 
class Invoice:
    """
    Represents a customer invoice with line items.
 
    Attributes:
        customer_id: The unique identifier of the customer.
        items: List of (description, amount) tuples.
        paid: Whether the invoice has been settled.
    """
 
    def __init__(self, customer_id: str) -> None:
        self.customer_id = customer_id
        self.items: list[tuple[str, float]] = []
        self.paid: bool = False

TODO and FIXME Comments

Use standardized tags for actionable comments that IDEs and linters can surface:

# TODO: Add caching to reduce repeated database lookups
def get_user(user_id: int):
    return db.query(f"SELECT * FROM users WHERE id = {user_id}")
 
# FIXME: This breaks when timezone is None — handle UTC fallback
def format_datetime(dt, timezone):
    return dt.astimezone(timezone).strftime("%Y-%m-%d %H:%M")
 
# NOTE: The API returns 204 for success, not 200
response = client.delete(f"/users/{user_id}")

Common Mistakes

  • Writing comments that restate the code instead of explaining intent
  • Leaving stale comments that describe what the code used to do
  • Using triple-quoted strings as block comments — they create string objects with overhead
  • Skipping docstrings on public functions and classes
  • Writing overly long inline comments that are hard to maintain

Best Practices

  • Comment the why, not the what — the code already shows what is happening
  • Keep docstrings up to date when you refactor function signatures
  • Use Google, NumPy, or Sphinx docstring style consistently across your project
  • Add type hints instead of using comments to describe parameter types
  • Run pydoc or use help() to verify your docstrings render correctly

Key Takeaways

  • Single-line comments use #; place them above the code they describe
  • Docstrings use triple quotes and become the __doc__ attribute of functions and classes
  • PEP 257 is the official Python convention for writing docstrings
  • Use Args, Returns, and Raises sections in docstrings for clarity
  • Avoid comments that repeat what the code already clearly expresses
  • # TODO and # FIXME tags are picked up by IDEs for issue tracking
  • Good comments explain intent and edge cases, not obvious operations

Advertisement

Sanjeev Sharma

Written by

Sanjeev Sharma

Full Stack Engineer · E-mopro

Related reading