85.2 Google Style Docstrings
Right, let’s talk about making your code explain itself. Because let’s be honest, you’re not going to write a 200-page manual for your 500-line script, and even if you did, no one would read it. The first and most crucial line of defense against the eternal question “what does this function do?” is the docstring. We’re going to focus on the Google-style docstring, not because it’s a holy edict from the mountain, but because it’s clean, readable, and has become a de facto standard for Python. It’s the sweet spot between the overly verbose (looking at you, Epytext) and the painfully sparse.
The goal here isn’t to please a pedantic linter; it’s to communicate. A great docstring tells me three things instantly: what the function needs, what it does, and what it gives back. If it can’t do that, the function is probably too complex.
The Basic Anatomy of a Google Docstring
It lives right under the function or class definition, wrapped in triple quotes. The structure is beautifully logical. Let’s start simple and then add the fancy bits.
def calculate_impact_force(mass, velocity, time_of_impact=0.1):
"""Calculates the force of an impact using the impulse-momentum theorem.
This is where you put your plain-English description. Assume the person
reading this is a competent developer who just doesn't know what *this*
specific function does. Explain the "why" here if it's not obvious.
Args:
mass (float): Mass of the object in kilograms. Must be non-negative.
velocity (float): Velocity of the object in meters per second.
time_of_impact (float, optional): The duration of the impact in seconds.
Defaults to 0.1s, because most things don't take a full second to smash.
Returns:
float: The calculated force in Newtons.
Raises:
ValueError: If mass or velocity are negative, because physics isn't a suggestion.
"""
if mass < 0 or velocity < 0:
raise ValueError("Mass and velocity must be non-negative. We don't do antimatter here.")
return mass * velocity / time_of_impact
See? No jargon, no nonsense. You immediately know what to give it and what you’ll get. The Args section is your function’s menu. The Returns section is your receipt.
Leveling Up: Handling Complex Scenarios
What if your function returns multiple things? Or takes a weird object? Google style has you covered.
def analyze_dataset(dataframe, verbose=False):
"""Performs a quick statistical summary of a Pandas DataFrame.
This function is a convenience wrapper around `describe()`,
but with some extra flair for the verbose folks.
Args:
dataframe (pd.DataFrame): The DataFrame to be analyzed. Cannot be empty.
verbose (bool, optional): If True, prints a summary to stdout.
Defaults to False.
Returns:
dict: A dictionary containing the following keys:
- `summary` (pd.DataFrame): The standard statistical summary.
- `is_empty` (bool): True if the input DataFrame had zero rows.
- `memory_usage` (str): Human-readable memory usage of the DataFrame.
Raises:
TypeError: If the input is not a Pandas DataFrame.
ValueError: If the input DataFrame is empty.
"""
if not isinstance(dataframe, pd.DataFrame):
raise TypeError("Hey, I asked for a DataFrame, not that.")
if dataframe.empty:
raise ValueError("This DataFrame is emptier than my motivation on a Monday.")
# ... function logic here ...
return {
"summary": dataframe.describe(),
"is_empty": dataframe.empty,
"memory_usage": f"{dataframe.memory_usage(deep=True).sum() / 1024**2:.2f} MB"
}
Notice the Returns section here. When you return a dictionary or a tuple, you must document what each element is. The person using your function shouldn’t have to guess what result[2] means.
The Forgotten Sections: Attributes, Yields, and Examples
This is where most docstrings fall flat. They document the function but forget the class attributes or the generator.
class ParticleAccelerator:
"""A class representing a very expensive and potentially dangerous coffee heater.
This is a class-level docstring. It should explain the purpose of the class itself.
Attributes:
energy_level (float): Current energy level in gigaelectronvolts (GeV).
beam_active (bool): True if the beam is currently on. Do not touch.
coolant_level (float): Percentage of coolant remaining. Alert if below 20%.
"""
def __init__(self, max_energy):
self.energy_level = 0.0
self.beam_active = False
self.coolant_level = 100.0
self._max_energy = max_energy
def generate_particles(self, target_energy):
"""Ramps up the accelerator to the target energy and yields particles.
This is a generator function, so we use 'Yields' instead of 'Returns'.
Args:
target_energy (float): The desired energy level to reach. Cannot exceed
the accelerator's maximum.
Yields:
str: A string representing a generated particle event.
Raises:
RuntimeError: If coolant levels are insufficient for the operation.
"""
if self.coolant_level < 20:
raise RuntimeError("Insufficient coolant! Aborting before we cause a mess.")
# ... ramp up logic ...
while self.energy_level < target_energy:
self.energy_level += 1
yield f"Particle generated at {self.energy_level} GeV"
Also, never underestimate the power of a good Example section. It’s like giving someone the cheat codes.
# ... inside a function ...
Examples:
>>> result = calculate_impact_force(10, 5)
>>> print(result)
500.0
>>> try:
... calculate_impact_force(-1, 5)
... except ValueError as e:
... print(e)
Mass and velocity must be non-negative...
Common Pitfalls and the One Unforgivable Sin
The biggest pitfall is lying in your docstring. If you change the function to return a tuple instead of a float, you must change the docstring. A docstring that lies is worse than no docstring at all—it’s actively malicious.
Other common mistakes:
- Vague descriptions: “Processes the data.” How? “Handles the object.” With what?
- Documenting the obvious:
name (str): The name of the person.No kidding. - Forgetting optional arguments: If it has a default value, it needs to be in
Argswith theoptionaltag. - Ignoring edge cases: If your function blows up on an empty list, document that behavior in
Raises.
The docstring isn’t academic busywork. It’s the most direct conversation you will have with the next person who touches your code, who is very often future-you. Future-you is forgetful and easily angered. Do future-you a favor. Write the docstring.