Python Basics — Docstrings & Annotations
Docstrings & Annotations
Well-documented functions are easier to use, maintain, and debug. Python has built-in support for documentation strings and type annotations.
Writing Docstrings
A docstring is a string literal that appears as the first statement in a function:
def add(a, b):
"""Return the sum of two numbers."""
return a + b
Multi-Line Docstrings
def divide(a, b):
"""
Divide a by b and return the result.
Returns None if b is zero.
"""
if b == 0:
return None
return a / b
Docstring Styles
Google Style (Recommended for Beginners)
def calculate_bmi(weight, height):
"""Calculate Body Mass Index.
Args:
weight: Weight in kilograms.
height: Height in meters.
Returns:
BMI as a float.
Raises:
ValueError: If height is zero or negative.
"""
if height <= 0:
raise ValueError("Height must be positive")
return weight / (height ** 2)
NumPy Style
def calculate_bmi(weight, height):
"""
Calculate Body Mass Index.
Parameters
----------
weight : float
Weight in kilograms.
height : float
Height in meters.
Returns
-------
float
BMI value.
Raises
------
ValueError
If height is zero or negative.
"""
if height <= 0:
raise ValueError("Height must be positive")
return weight / (height ** 2)
reStructuredText Style
def calculate_bmi(weight, height):
"""Calculate Body Mass Index.
:param weight: Weight in kilograms.
:type weight: float
:param height: Height in meters.
:type height: float
:returns: BMI value.
:rtype: float
:raises ValueError: If height is zero or negative.
"""
if height <= 0:
raise ValueError("Height must be positive")
return weight / (height ** 2)
Type Hints / Annotations
Python 3.5+ supports type annotations — hints about what types a function expects and returns:
def add(a: int, b: int) -> int:
return a + b
def greet(name: str, times: int = 1) -> str:
return (f"Hello, {name}! " * times).strip()
def is_even(n: int) -> bool:
return n % 2 == 0
Type hints are not enforced at runtime — they’re for documentation and IDE support.
Common Type Annotations
from typing import Optional, Union
def find_user(user_id: int) -> Optional[dict]:
"""Return user dict or None if not found."""
pass
def process(value: Union[str, int]) -> str:
"""Accept string or int, return string."""
return str(value)
# Python 3.10+ simplified syntax
def process(value: str | int) -> str:
return str(value)
Collection Types
def average(numbers: list[float]) -> float:
return sum(numbers) / len(numbers)
def count_words(text: str) -> dict[str, int]:
words = text.split()
return {w: words.count(w) for w in set(words)}
help() and __doc__
def factorial(n):
"""Calculate the factorial of n (n! = n × (n-1) × ... × 1)."""
if n <= 1:
return 1
return n * factorial(n - 1)
# Access docstring
print(factorial.__doc__)
# Calculate the factorial of n (n! = n × (n-1) × ... × 1).
# Interactive help
help(factorial)
# Help on function factorial:
# factorial(n)
# Calculate the factorial of n (n! = n × (n-1) × ... × 1).
Combining Docstrings and Type Hints
def search(items: list[str], query: str, case_sensitive: bool = False) -> list[str]:
"""Search for items matching a query string.
Args:
items: List of strings to search through.
query: The search term.
case_sensitive: Whether the search is case-sensitive. Defaults to False.
Returns:
List of matching items.
"""
if not case_sensitive:
query = query.lower()
return [item for item in items if query in item.lower()]
return [item for item in items if query in item]
Why Documentation Matters
# ❌ Undocumented — what does this do?
def proc(d, k, v):
if k in d:
d[k].append(v)
else:
d[k] = [v]
return d
# ✅ Documented — clear purpose and usage
def group_by_key(data: dict, key: str, value) -> dict:
"""Add a value to a grouped dictionary.
If the key exists, appends to the existing list.
If not, creates a new list with the value.
Args:
data: The dictionary to update.
key: The grouping key.
value: The value to add.
Returns:
The updated dictionary.
"""
if key in data:
data[key].append(value)
else:
data[key] = [value]
return data
Summary
- Docstrings: first line of a function, in triple quotes
- Google style is most beginner-friendly
- Type hints:
def f(x: int) -> str:— not enforced at runtime - Access with
func.__doc__orhelp(func) - Use
Optional[X]for values that could beNone - Good documentation saves hours of debugging
Next up: Functions — Practice Exercises →