Writing Function and Class Level Docstring Profiles
π·οΈ Python Scripting Best Practices / Meaningful Comments and Docstrings
When you're building Python scripts that others will useβor that you'll revisit months laterβclear documentation is essential. Docstrings are your best tool for explaining what a function or class does, how to use it, and what to expect back. This guide walks through writing effective docstrings at both the function and class level.
π§ Why Docstrings Matter
- Self-documenting code β Docstrings turn your code into readable documentation without extra effort.
- Tool-friendly β Tools like pydoc, Sphinx, and IDE help popups read docstrings automatically.
- Team collaboration β New engineers can understand your code's purpose without reading every line.
- Maintainability β When you revisit old code, docstrings save you from guessing your own intentions.
βοΈ Function-Level Docstrings
Every function should have a docstring that explains:
- What the function does (one-line summary)
- Parameters β names, expected types, and what they represent
- Return value β what the function gives back, including type
- Raises β any exceptions that might occur
Basic structure for a function docstring:
- First line: A brief, imperative statement of what the function does.
- Blank line (if adding more detail)
- Args section: List each parameter with its type and description.
- Returns section: Describe the return value and its type.
- Raises section (optional): List exceptions and when they occur.
Example β Simple function docstring:
- Function:
calculate_disk_usage - Docstring content:
- Summary: Calculate the percentage of disk space used on a given mount point.
- Args: mount_point (str) β The filesystem path to check.
- Returns: float β Percentage of disk space used (0.0 to 100.0).
- Raises: FileNotFoundError β If the mount point does not exist.
Example β Function with multiple parameters:
- Function:
parse_config_file - Docstring content:
- Summary: Read and parse a configuration file into a dictionary.
- Args: file_path (str) β Absolute path to the config file.
- encoding (str, optional) β File encoding. Defaults to 'utf-8'.
- Returns: dict β Parsed key-value pairs from the config file.
- Raises: ValueError β If the file format is invalid.
ποΈ Class-Level Docstrings
Class docstrings explain the purpose of the class and how it should be used. They often include:
- Class summary β What the class represents or manages
- Attributes β Key instance variables and their types
- Usage example β Brief demonstration of how to instantiate and use the class
Basic structure for a class docstring:
- First line: A concise description of the class's responsibility.
- Blank line (if adding more detail)
- Attributes section: List important instance attributes with types and descriptions.
- Usage example (optional): Show a quick instantiation and method call.
Example β Simple class docstring:
- Class:
ServerMonitor - Docstring content:
- Summary: Monitor CPU, memory, and disk metrics for a remote server.
- Attributes: hostname (str) β The server's hostname or IP address.
- timeout (int) β Connection timeout in seconds.
- Usage: monitor = ServerMonitor('web-01', timeout=30)
- monitor.check_health()
Example β Class with inheritance:
- Class:
DatabaseConnector - Docstring content:
- Summary: Base class for connecting to various database backends.
- Attributes: connection_string (str) β Database connection URI.
- connection (object) β Active database connection object.
- Note: Subclasses must implement the connect() and disconnect() methods.
π Function vs. Class Docstring Comparison
| Aspect | Function Docstring | Class Docstring |
|---|---|---|
| Primary focus | What the function does | What the class represents |
| Key sections | Args, Returns, Raises | Attributes, Usage example |
| Tone | Action-oriented (imperative) | Descriptive (noun-focused) |
| Length | Usually 3β10 lines | Often 5β15 lines |
| Example included? | Rarely | Frequently |
π οΈ Best Practices for Writing Docstrings
- Keep the summary line short β Aim for 50β80 characters. It should stand alone.
- Use consistent formatting β Stick with one style (Google, NumPy, or reStructuredText) across your project.
- Describe why, not just what β Explain the purpose, not just the mechanics.
- Update docstrings when code changes β Stale docstrings are worse than none.
- Include edge cases β Mention what happens with empty inputs,
None, or invalid data. - Use type hints alongside docstrings β Type hints in the function signature complement the docstring's type info.
π΅οΈ Common Docstring Pitfalls to Avoid
- Writing obvious comments β Don't restate what the code clearly shows.
- Forgetting to document exceptions β Engineers need to know what can break.
- Using vague language β Avoid words like stuff, things, or handle.
- Mixing docstring styles β Choose one format and use it everywhere.
- Leaving docstrings blank β A placeholder like TODO is better than nothing.
β Quick Checklist for Your Docstrings
- [ ] Does the first line clearly state the function's or class's purpose?
- [ ] Are all parameters documented with types and descriptions?
- [ ] Is the return value described, including its type?
- [ ] Are exceptions listed with conditions that trigger them?
- [ ] For classes, are key attributes documented?
- [ ] Is the docstring up to date with the current code?
π Final Thoughts
Docstrings are a small investment that pays off every time someone reads your codeβincluding your future self. By writing clear function and class-level docstrings, you make your Python scripts more accessible, maintainable, and professional. Start with the simple structures above, and refine as your projects grow.
Docstrings are multi-line comments placed inside functions and classes that describe what they do, used by Python's help system and documentation tools.
π Example 1: Basic Function Docstring
This example shows the simplest docstring format for a single-purpose function.
def add_numbers(a, b):
"""Return the sum of two numbers."""
return a + b
print(add_numbers(3, 5))
π€ Output: 8
π Example 2: Multi-line Function Docstring with Parameters
This example demonstrates a docstring that describes parameters and return value using the standard format.
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
print(calculate_area(4.5, 3.2))
π€ Output: 14.4
π Example 3: Class Docstring with Constructor
This example shows a class-level docstring describing the class purpose and its constructor parameters.
class TemperatureSensor:
"""Represents a temperature sensor that reads and stores temperature values."""
def __init__(self, location):
"""
Initialize the temperature sensor with a location name.
Parameters:
location (str): The physical location of the sensor.
"""
self.location = location
self.readings = []
def record_reading(self, value):
"""Add a temperature reading to the sensor's history."""
self.readings.append(value)
sensor = TemperatureSensor("Engine Room")
sensor.record_reading(85.3)
print(sensor.readings)
π€ Output: [85.3]
π Example 4: Function Docstring with Multiple Returns and Raises
This example demonstrates documenting different return conditions and possible exceptions.
def divide_numbers(dividend, divisor):
"""
Divide two numbers and return the result.
Parameters:
dividend (float): The number to be divided.
divisor (float): The number to divide by.
Returns:
float: The quotient of the division.
Raises:
ValueError: If divisor is zero.
"""
if divisor == 0:
raise ValueError("Cannot divide by zero")
return dividend / divisor
try:
result = divide_numbers(10, 0)
except ValueError as error:
print(error)
π€ Output: Cannot divide by zero
π Example 5: Class with Multiple Methods and Property Docstrings
This example shows a practical class with documented methods and a property that engineers might use for monitoring.
class PumpMonitor:
"""Monitors pump status and tracks operational metrics."""
def __init__(self, pump_id):
"""
Initialize the pump monitor with a unique identifier.
Parameters:
pump_id (str): The unique identifier for this pump.
"""
self.pump_id = pump_id
self._running = False
self._hours_run = 0.0
def start_pump(self):
"""Start the pump and begin tracking run time."""
self._running = True
def stop_pump(self):
"""Stop the pump and record the current run time."""
self._running = False
@property
def total_run_hours(self):
"""
Get the total number of hours the pump has been running.
Returns:
float: Total run hours accumulated.
"""
return self._hours_run
def add_run_time(self, hours):
"""
Add run time to the pump's total.
Parameters:
hours (float): Number of hours to add.
"""
self._hours_run += hours
pump = PumpMonitor("PUMP-101")
pump.start_pump()
pump.add_run_time(12.5)
pump.stop_pump()
print(f"Pump {pump.pump_id} ran for {pump.total_run_hours} hours")
π€ Output: Pump PUMP-101 ran for 12.5 hours
π Docstring Format Comparison
| Feature | One-Line Docstring | Multi-Line Docstring |
|---|---|---|
| Best for | Simple functions with clear purpose | Complex functions or classes |
| Structure | Single line in triple quotes | Summary line, blank line, then details |
| Parameter docs | Not included | Included with types and descriptions |
| Return docs | Not included | Included with type and description |
| Raises docs | Not included | Included when exceptions are raised |
| Example use | def add(a, b): """Return sum.""" |
def calc(x): """... Parameters: ... Returns: ...""" |
When you're building Python scripts that others will useβor that you'll revisit months laterβclear documentation is essential. Docstrings are your best tool for explaining what a function or class does, how to use it, and what to expect back. This guide walks through writing effective docstrings at both the function and class level.
π§ Why Docstrings Matter
- Self-documenting code β Docstrings turn your code into readable documentation without extra effort.
- Tool-friendly β Tools like pydoc, Sphinx, and IDE help popups read docstrings automatically.
- Team collaboration β New engineers can understand your code's purpose without reading every line.
- Maintainability β When you revisit old code, docstrings save you from guessing your own intentions.
βοΈ Function-Level Docstrings
Every function should have a docstring that explains:
- What the function does (one-line summary)
- Parameters β names, expected types, and what they represent
- Return value β what the function gives back, including type
- Raises β any exceptions that might occur
Basic structure for a function docstring:
- First line: A brief, imperative statement of what the function does.
- Blank line (if adding more detail)
- Args section: List each parameter with its type and description.
- Returns section: Describe the return value and its type.
- Raises section (optional): List exceptions and when they occur.
Example β Simple function docstring:
- Function:
calculate_disk_usage - Docstring content:
- Summary: Calculate the percentage of disk space used on a given mount point.
- Args: mount_point (str) β The filesystem path to check.
- Returns: float β Percentage of disk space used (0.0 to 100.0).
- Raises: FileNotFoundError β If the mount point does not exist.
Example β Function with multiple parameters:
- Function:
parse_config_file - Docstring content:
- Summary: Read and parse a configuration file into a dictionary.
- Args: file_path (str) β Absolute path to the config file.
- encoding (str, optional) β File encoding. Defaults to 'utf-8'.
- Returns: dict β Parsed key-value pairs from the config file.
- Raises: ValueError β If the file format is invalid.
ποΈ Class-Level Docstrings
Class docstrings explain the purpose of the class and how it should be used. They often include:
- Class summary β What the class represents or manages
- Attributes β Key instance variables and their types
- Usage example β Brief demonstration of how to instantiate and use the class
Basic structure for a class docstring:
- First line: A concise description of the class's responsibility.
- Blank line (if adding more detail)
- Attributes section: List important instance attributes with types and descriptions.
- Usage example (optional): Show a quick instantiation and method call.
Example β Simple class docstring:
- Class:
ServerMonitor - Docstring content:
- Summary: Monitor CPU, memory, and disk metrics for a remote server.
- Attributes: hostname (str) β The server's hostname or IP address.
- timeout (int) β Connection timeout in seconds.
- Usage: monitor = ServerMonitor('web-01', timeout=30)
- monitor.check_health()
Example β Class with inheritance:
- Class:
DatabaseConnector - Docstring content:
- Summary: Base class for connecting to various database backends.
- Attributes: connection_string (str) β Database connection URI.
- connection (object) β Active database connection object.
- Note: Subclasses must implement the connect() and disconnect() methods.
π Function vs. Class Docstring Comparison
| Aspect | Function Docstring | Class Docstring |
|---|---|---|
| Primary focus | What the function does | What the class represents |
| Key sections | Args, Returns, Raises | Attributes, Usage example |
| Tone | Action-oriented (imperative) | Descriptive (noun-focused) |
| Length | Usually 3β10 lines | Often 5β15 lines |
| Example included? | Rarely | Frequently |
π οΈ Best Practices for Writing Docstrings
- Keep the summary line short β Aim for 50β80 characters. It should stand alone.
- Use consistent formatting β Stick with one style (Google, NumPy, or reStructuredText) across your project.
- Describe why, not just what β Explain the purpose, not just the mechanics.
- Update docstrings when code changes β Stale docstrings are worse than none.
- Include edge cases β Mention what happens with empty inputs,
None, or invalid data. - Use type hints alongside docstrings β Type hints in the function signature complement the docstring's type info.
π΅οΈ Common Docstring Pitfalls to Avoid
- Writing obvious comments β Don't restate what the code clearly shows.
- Forgetting to document exceptions β Engineers need to know what can break.
- Using vague language β Avoid words like stuff, things, or handle.
- Mixing docstring styles β Choose one format and use it everywhere.
- Leaving docstrings blank β A placeholder like TODO is better than nothing.
β Quick Checklist for Your Docstrings
- [ ] Does the first line clearly state the function's or class's purpose?
- [ ] Are all parameters documented with types and descriptions?
- [ ] Is the return value described, including its type?
- [ ] Are exceptions listed with conditions that trigger them?
- [ ] For classes, are key attributes documented?
- [ ] Is the docstring up to date with the current code?
π Final Thoughts
Docstrings are a small investment that pays off every time someone reads your codeβincluding your future self. By writing clear function and class-level docstrings, you make your Python scripts more accessible, maintainable, and professional. Start with the simple structures above, and refine as your projects grow.
Interactive Views
You are currently in π All-in-One mode. Use the tabs at the top to switch to π Theory Only or π» Code Only views.
Docstrings are multi-line comments placed inside functions and classes that describe what they do, used by Python's help system and documentation tools.
π Example 1: Basic Function Docstring
This example shows the simplest docstring format for a single-purpose function.
def add_numbers(a, b):
"""Return the sum of two numbers."""
return a + b
print(add_numbers(3, 5))
π€ Output: 8
π Example 2: Multi-line Function Docstring with Parameters
This example demonstrates a docstring that describes parameters and return value using the standard format.
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
print(calculate_area(4.5, 3.2))
π€ Output: 14.4
π Example 3: Class Docstring with Constructor
This example shows a class-level docstring describing the class purpose and its constructor parameters.
class TemperatureSensor:
"""Represents a temperature sensor that reads and stores temperature values."""
def __init__(self, location):
"""
Initialize the temperature sensor with a location name.
Parameters:
location (str): The physical location of the sensor.
"""
self.location = location
self.readings = []
def record_reading(self, value):
"""Add a temperature reading to the sensor's history."""
self.readings.append(value)
sensor = TemperatureSensor("Engine Room")
sensor.record_reading(85.3)
print(sensor.readings)
π€ Output: [85.3]
π Example 4: Function Docstring with Multiple Returns and Raises
This example demonstrates documenting different return conditions and possible exceptions.
def divide_numbers(dividend, divisor):
"""
Divide two numbers and return the result.
Parameters:
dividend (float): The number to be divided.
divisor (float): The number to divide by.
Returns:
float: The quotient of the division.
Raises:
ValueError: If divisor is zero.
"""
if divisor == 0:
raise ValueError("Cannot divide by zero")
return dividend / divisor
try:
result = divide_numbers(10, 0)
except ValueError as error:
print(error)
π€ Output: Cannot divide by zero
π Example 5: Class with Multiple Methods and Property Docstrings
This example shows a practical class with documented methods and a property that engineers might use for monitoring.
class PumpMonitor:
"""Monitors pump status and tracks operational metrics."""
def __init__(self, pump_id):
"""
Initialize the pump monitor with a unique identifier.
Parameters:
pump_id (str): The unique identifier for this pump.
"""
self.pump_id = pump_id
self._running = False
self._hours_run = 0.0
def start_pump(self):
"""Start the pump and begin tracking run time."""
self._running = True
def stop_pump(self):
"""Stop the pump and record the current run time."""
self._running = False
@property
def total_run_hours(self):
"""
Get the total number of hours the pump has been running.
Returns:
float: Total run hours accumulated.
"""
return self._hours_run
def add_run_time(self, hours):
"""
Add run time to the pump's total.
Parameters:
hours (float): Number of hours to add.
"""
self._hours_run += hours
pump = PumpMonitor("PUMP-101")
pump.start_pump()
pump.add_run_time(12.5)
pump.stop_pump()
print(f"Pump {pump.pump_id} ran for {pump.total_run_hours} hours")
π€ Output: Pump PUMP-101 ran for 12.5 hours
π Docstring Format Comparison
| Feature | One-Line Docstring | Multi-Line Docstring |
|---|---|---|
| Best for | Simple functions with clear purpose | Complex functions or classes |
| Structure | Single line in triple quotes | Summary line, blank line, then details |
| Parameter docs | Not included | Included with types and descriptions |
| Return docs | Not included | Included with type and description |
| Raises docs | Not included | Included when exceptions are raised |
| Example use | def add(a, b): """Return sum.""" |
def calc(x): """... Parameters: ... Returns: ...""" |