PEP 257: Docstring Conventions for Self-Documenting Code
TL;DR
PEP 257 defines standardized conventions for Python docstrings, covering how to document modules, functions, classes, and methods using string literals that become the __doc__ attribute. The conventions emphasize triple double quotes, command-style phrasing, and consistent formatting.
Interesting!
PEP 257 itself acknowledges these are conventions, not laws: “If you violate these conventions, the worst you’ll get is some dirty looks”. But as the PEP 20 Zen of Python principle 7 notes - “readability counts”!
What Makes a Good Docstring?
PEP 257 distinguishes between conventions (recommended practices) and requirements. All modules, exported functions, classes, and public methods should include docstrings as their first statement.
python code snippet start
def get_user_by_id(user_id):
"""Return user object for the given ID."""
return database.users.find_one({"_id": user_id})
# Access the docstring at runtime
print(get_user_by_id.__doc__)
# Output: Return user object for the given ID.python code snippet end
One-Line Docstrings
For simple, obvious cases, one-line docstrings keep both opening and closing quotes on the same line:
python code snippet start
# Good - prescriptive command ending with period
def clear_cache():
"""Clear all cached data from memory."""
cache.clear()
# Bad - descriptive instead of prescriptive
def clear_cache():
"""This function clears the cache."""
cache.clear()
# Bad - restates the obvious signature
def square(x):
"""square(x) -> x squared""" # Don't repeat signature
return x * x
# Good - describes what it returns
def square(x):
"""Return the square of x."""
return x * xpython code snippet end
Key principles for one-liners:
- Use commands (“Return”, “Calculate”, “Send”) not descriptions
- End with a period
- Don’t restate the function signature
- Focus on the return value when applicable
Multi-Line Docstrings
Multi-line docstrings start with a summary line, followed by a blank line, then detailed information:
python code snippet start
def calculate_compound_interest(principal, rate, years, compound_freq=1):
"""Calculate compound interest with flexible compounding frequency.
This function computes the final amount after applying compound
interest over a specified period with customizable compounding frequency.
Args:
principal (float): Initial investment amount in dollars
rate (float): Annual interest rate as decimal (0.05 for 5%)
years (int): Number of years to compound
compound_freq (int): Times per year to compound (default: 1)
Returns:
float: Final amount after compound interest
Raises:
ValueError: If principal or rate is negative
"""
if principal < 0 or rate < 0:
raise ValueError("Principal and rate must be non-negative")
return principal * (1 + rate / compound_freq) ** (years * compound_freq)python code snippet end
Class and Method Docstrings
Class docstrings summarize behavior and list public methods, while the __init__ constructor gets its own docstring:
python code snippet start
class UserAccountManager:
"""Manage user account operations and authentication.
This class provides methods for creating, validating, and managing
user accounts including password verification and session handling.
Public Methods:
create_account: Register a new user account
authenticate: Verify user credentials
reset_password: Initiate password reset process
Attributes:
max_attempts (int): Maximum login attempts before lockout
"""
def __init__(self, max_attempts=3):
"""Initialize the account manager.
Args:
max_attempts (int): Maximum login attempts allowed (default: 3)
"""
self.max_attempts = max_attempts
def authenticate(self, username, password):
"""Verify user credentials and create session.
Args:
username (str): User's login name
password (str): User's password (will be hashed)
Returns:
Session: Active session object if successful
Raises:
AuthenticationError: If credentials are invalid
"""
# Implementation here
passpython code snippet end
Module Docstrings
Module docstrings appear at the top of files and describe the module’s purpose and exported items:
python code snippet start
"""User authentication and authorization utilities.
This module provides functions and classes for managing user authentication,
including password hashing, session management, and permission checking.
Exported Classes:
UserAccountManager: Main authentication handler
Session: User session representation
Exported Functions:
hash_password: Securely hash passwords with salt
verify_password: Compare password against stored hash
Example:
>>> from auth import UserAccountManager
>>> manager = UserAccountManager()
>>> session = manager.authenticate('alice', 'secret123')
"""
import hashlib
from datetime import datetimepython code snippet end
Override vs Extend
When documenting inherited methods, PEP 257 distinguishes between replacement and augmentation:
python code snippet start
class BaseHandler:
def process(self, data):
"""Process incoming data and return result."""
return self.transform(data)
class CustomHandler(BaseHandler):
def process(self, data):
"""Override process to add validation step."""
if not self.validate(data):
raise ValueError("Invalid data")
return super().process(data)
class LoggingHandler(BaseHandler):
def process(self, data):
"""Extend process to log all operations."""
result = super().process(data)
self.log_operation(data, result)
return resultpython code snippet end
Formatting Best Practices
PEP 257 emphasizes consistency over dogma:
python code snippet start
# Good - closing quotes on separate line for multi-line
def complex_operation():
"""Perform complex multi-step operation.
This function coordinates several subsystems to achieve
the desired outcome with proper error handling.
"""
pass
# Good - stay on one line if it fits
def simple_operation():
"""Return True if operation succeeds."""
return True
# Bad - don't use uppercase for parameter names in prose
def bad_docs(user_id):
"""Find user with USER_ID.""" # Don't do this
pass
# Good - use lowercase in prose
def good_docs(user_id):
"""Find user with the given user_id."""
passpython code snippet end
PEP 257 provides the foundation for self-documenting Python code where documentation lives alongside implementation. Following these conventions ensures your docstrings work seamlessly with help systems, IDE completion, and documentation generators.
These documentation standards perfectly complement PEP 8's code formatting rules to create consistently styled, well-documented Python code. Docstrings are particularly important when defining Python classes and reusable functions .
Reference: PEP 257 - Docstring Conventions