Docstrings¶
In this tutorial, you will learn how to document your functions using docstrings. A well-written docstring explains what a function does, what it expects, and what it returns — making your code easier to use and maintain.
Time commitment: 15–20 minutes
Prerequisites:
- Python 3.12 or later installed on your machine
- Completion of Tutorial 01 — Defining functions
- Completion of Tutorial 02 — Lambda expressions
- Completion of Tutorial 03 — Type hints
Learning objectives¶
By the end of this tutorial, you will be able to:
- Write single-line and multi-line docstrings
- Follow PEP 257 conventions
- Use the Google docstring format for parameters and return values
- Access docstrings using
help()and__doc__
What is a docstring?¶
A docstring is a string literal that appears as the first statement inside a function (or class, or module). Python stores it in a special attribute called __doc__, which tools like help() use to display documentation.
Unlike comments (which start with #), docstrings are part of the running program. They can be accessed at runtime, which makes them much more powerful than ordinary comments.
def greet(name: str) -> str:
"""Return a greeting for the given name."""
return f"Hello, {name}!"
print(greet("Alice"))
The string """Return a greeting for the given name.""" is a docstring. It uses triple double quotes and sits immediately after the def line. This is the simplest form of documentation you can add to a function.
Single-line docstrings¶
For simple functions, a single-line docstring is sufficient. According to PEP 257, it should:
- Use triple double quotes (
"""...""") - Fit on one line, including the quotes
- Be a phrase ending with a full stop
- Describe what the function does, not how
- Use the imperative mood ("Return" not "Returns")
Here are some good examples:
def square(n: int) -> int:
"""Return the square of n."""
return n ** 2
def is_even(n: int) -> bool:
"""Check whether n is an even number."""
return n % 2 == 0
def celsius_to_fahrenheit(celsius: float) -> float:
"""Convert a temperature from Celsius to Fahrenheit."""
return celsius * 9 / 5 + 32
print(square(4))
print(is_even(7))
print(celsius_to_fahrenheit(100))
Notice the imperative mood: "Return the square", "Check whether", "Convert a temperature". This reads as a command — telling the function what to do.
Multi-line docstrings¶
When a function is more complex, you need more space to describe it. A multi-line docstring has three parts:
- A summary line — a single sentence describing the function
- A blank line separating the summary from the body
- An elaboration with additional details
The closing """ goes on its own line:
def calculate_bmi(weight_kg: float, height_m: float) -> float:
"""Calculate the Body Mass Index (BMI) for the given measurements.
The BMI is calculated by dividing weight in kilograms by the square
of height in metres. The result is rounded to one decimal place.
"""
return round(weight_kg / (height_m ** 2), 1)
print(calculate_bmi(70, 1.75))
The summary line ("Calculate the Body Mass Index...") stands alone as a complete description. The elaboration provides additional context about the formula and rounding.
PEP 257 conventions¶
PEP 257 is the Python standard for docstring conventions. Here are the key rules:
- Always use triple double quotes:
"""...""" - Write the summary in the imperative mood: "Return", "Calculate", "Check" (not "Returns", "Calculates", "Checks")
- The summary should be a complete sentence ending with a full stop
- For multi-line docstrings, leave a blank line after the summary
- The closing
"""should be on its own line for multi-line docstrings - Do not leave a blank line before the closing
"""
Here is an example showing common mistakes and the corrected version:
# Incorrect — uses "Returns" instead of "Return", no full stop
def bad_example(x: int) -> int:
"""Returns the absolute value of x"""
return abs(x)
# Correct — imperative mood, ends with a full stop
def good_example(x: int) -> int:
"""Return the absolute value of x."""
return abs(x)
print(good_example(-42))
Google docstring format¶
PEP 257 tells you how to structure a docstring, but it does not specify how to document parameters and return values. Several formats exist for this purpose. The Google format is widely used because it is clean and easy to read.
The Args section¶
Use the Args section to document each parameter:
def calculate_discount(price: float, discount_percent: float) -> float:
"""Calculate the discounted price of an item.
Args:
price: The original price of the item in pounds.
discount_percent: The discount percentage (for example, 20 for 20%).
Returns:
The price after applying the discount, rounded to two decimal places.
"""
discount_amount = price * (discount_percent / 100)
return round(price - discount_amount, 2)
print(calculate_discount(49.99, 20))
Each parameter is listed on its own line, indented under Args:, with a colon separating the name from the description.
The Returns section¶
The Returns section describes what the function gives back. For simple return values, a single sentence is enough. For complex return values, you can add more detail:
def split_name(full_name: str) -> tuple[str, str]:
"""Split a full name into first name and surname.
Assume the full name consists of exactly two parts separated by a space.
Args:
full_name: A string containing a first name and surname
separated by a single space.
Returns:
A tuple containing the first name and the surname.
"""
parts = full_name.split(" ", 1)
return (parts[0], parts[1])
first, last = split_name("Ada Lovelace")
print(f"First: {first}, Surname: {last}")
The Raises section¶
If a function can raise exceptions, document them in the Raises section:
def divide(a: float, b: float) -> float:
"""Divide a by b and return the result.
Args:
a: The numerator.
b: The denominator. Must not be zero.
Returns:
The result of dividing a by b.
Raises:
ValueError: If b is zero.
"""
if b == 0:
raise ValueError("Cannot divide by zero.")
return a / b
print(divide(10, 3))
try:
divide(5, 0)
except ValueError as error:
print(f"Error: {error}")
help(calculate_discount)
The __doc__ attribute¶
Every function has a __doc__ attribute that contains the raw docstring text:
print(calculate_discount.__doc__)
The inspect.getdoc() function¶
The inspect module provides getdoc(), which cleans up the indentation of the docstring for nicer display:
import inspect
print(inspect.getdoc(calculate_discount))
The difference between __doc__ and inspect.getdoc() is subtle: getdoc() strips the leading indentation from multi-line docstrings, producing a cleaner result.
Docstrings and type hints together¶
Type hints and docstrings complement each other beautifully. Type hints specify what types a function works with, while docstrings explain what the function does and what the values mean.
When you use both, you do not need to repeat the type information in the docstring — the type hints handle that:
def create_invoice(
customer_name: str,
items: list[tuple[str, float]],
tax_rate: float = 0.2,
) -> dict[str, str | float | list[tuple[str, float]]]:
"""Create an invoice for a customer.
Build an invoice dictionary from the given customer name and list
of items. The total is calculated including tax.
Args:
customer_name: The full name of the customer.
items: A list of tuples, where each tuple contains an item
name and its price in pounds.
tax_rate: The tax rate as a decimal. Defaults to 0.2 (20%).
Returns:
A dictionary containing the customer name, items, subtotal,
tax amount, and total.
"""
subtotal = sum(price for _, price in items)
tax = round(subtotal * tax_rate, 2)
total = round(subtotal + tax, 2)
return {
"customer": customer_name,
"items": items,
"subtotal": subtotal,
"tax": tax,
"total": total,
}
invoice = create_invoice("Alice Smith", [("Notebook", 4.99), ("Pen", 1.50)])
for key, value in invoice.items():
print(f"{key}: {value}")
Notice that the docstring does not say "customer_name (str)" — the type hint already provides that information. The docstring focuses on describing the meaning and purpose of each parameter.
Documenting default arguments¶
When a function has parameters with default values, it is good practice to mention the default in the docstring. This helps users who read the docstring through help() without looking at the source code.
Here are two approaches:
def format_currency(amount: float, symbol: str = "\u00a3", decimals: int = 2) -> str:
"""Format a number as a currency string.
Args:
amount: The monetary amount to format.
symbol: The currency symbol to prepend. Defaults to '\u00a3'.
decimals: The number of decimal places. Defaults to 2.
Returns:
A formatted currency string.
"""
return f"{symbol}{amount:.{decimals}f}"
print(format_currency(29.99))
print(format_currency(1250, "$", 0))
print(format_currency(9.5, decimals=1))
The phrase "Defaults to" is a clear, consistent way to document default values. You can see this pattern used throughout the Python standard library documentation.
Exercise¶
The following functions have type hints but no docstrings. Add a complete Google-format docstring to each one, including Args, Returns, and Raises sections where appropriate.
def calculate_area(length: float, width: float) -> float:
return length * width
def find_maximum(numbers: list[int]) -> int:
if not numbers:
raise ValueError("The list must not be empty.")
result = numbers[0]
for n in numbers[1:]:
if n > result:
result = n
return result
def format_name(first: str, last: str, title: str = "") -> str:
if title:
return f"{title} {first} {last}"
return f"{first} {last}"
# Add docstrings to each function below
def calculate_area(length: float, width: float) -> float:
return length * width
def find_maximum(numbers: list[int]) -> int:
if not numbers:
raise ValueError("The list must not be empty.")
result = numbers[0]
for n in numbers[1:]:
if n > result:
result = n
return result
def format_name(first: str, last: str, title: str = "") -> str:
if title:
return f"{title} {first} {last}"
return f"{first} {last}"
# Test your documented functions
# print(calculate_area(5.0, 3.0))
# print(find_maximum([3, 7, 2, 9, 4]))
# print(format_name("Ada", "Lovelace", "Lady"))
# help(calculate_area)
Solution¶
def calculate_area(length: float, width: float) -> float:
"""Calculate the area of a rectangle.
Args:
length: The length of the rectangle in metres.
width: The width of the rectangle in metres.
Returns:
The area of the rectangle.
"""
return length * width
def find_maximum(numbers: list[int]) -> int:
"""Find the largest number in a list.
Iterate through the list and return the number with the
highest value.
Args:
numbers: A non-empty list of integers to search.
Returns:
The largest integer in the list.
Raises:
ValueError: If the list is empty.
"""
if not numbers:
raise ValueError("The list must not be empty.")
result = numbers[0]
for n in numbers[1:]:
if n > result:
result = n
return result
def format_name(first: str, last: str, title: str = "") -> str:
"""Format a person's full name with an optional title.
Args:
first: The person's first name.
last: The person's surname.
title: An honorific or title to prepend. Defaults to an
empty string.
Returns:
The formatted full name, with the title prepended if provided.
"""
if title:
return f"{title} {first} {last}"
return f"{first} {last}"
print(calculate_area(5.0, 3.0))
print(find_maximum([3, 7, 2, 9, 4]))
print(format_name("Ada", "Lovelace", "Lady"))
print(format_name("Grace", "Hopper"))
# Verify the docstrings are accessible
help(find_maximum)
Key points from the solution:
- Each docstring starts with an imperative summary line ending with a full stop
find_maximumincludes aRaisessection because it can raise aValueErrorformat_namedocuments the default value for thetitleparameter- Multi-line descriptions within
Argsare indented to align with the first line
Summary¶
In this tutorial, you learned how to:
- Write single-line docstrings for simple functions using triple double quotes
- Write multi-line docstrings with a summary line, blank line, and elaboration
- Follow PEP 257 conventions: imperative mood, triple double quotes, and proper formatting
- Use the Google docstring format with
Args,Returns, andRaisessections - Access docstrings at runtime using
help(),__doc__, andinspect.getdoc() - Combine docstrings with type hints without duplicating type information
- Document default argument values clearly using "Defaults to" phrasing
What is next¶
In the next tutorial, Scope and closures, you will learn how Python resolves variable names using the LEGB rule and how closures capture variables from enclosing scopes. These concepts are essential for understanding how functions interact with the data around them.