Chapter Relevance
Lab Relevance: ★☆☆ (Relevant for properly documenting your custom tools and passing assignments)
Project Relevance: ★★☆ (Important for collaborative group projects and reproducible research)
Foundation: ★★☆ (A basic professional standard in software engineering and data science)
Time to Read: 10 minutes
In a nutshell: Master the art of writing self-explanatory, professional functions using NumPy-style docstrings, and learn how to avoid the most common bugs that break Python pipelines.
Skip this if: You already strictly use NumPy-style docstrings to document your code and are completely familiar with common function pitfalls like mutable defaults and missing return statements.
By this point, you know how to build powerful, flexible spatial tools. But as your scripts grow into larger pipelines, relying solely on memory to remember what each function does becomes impossible. In this final section, we will learn how to formally document our code and review the most common mistakes that break pipelines.
1. Docstrings: The Standard of Reproducibility¶
When you write functions, it is best practice to include built-in documentation that explains what the function does, what inputs it expects, and what output it returns.
In Python, this is done using a docstring (documentation string). A docstring is a multi-line string enclosed in triple double quotes (""" """) placed immediately below the def statement.
While there are several formatting styles for docstrings, the NumPy style is the gold standard in the data science community. It uses clear, readable sections.
Let’s document our haversine distance calculator:
import math
def haversine(lat1, lon1, lat2, lon2):
"""
Calculates the great-circle distance between two geographic coordinates.
This function assumes a spherical Earth with a radius of 6371.0 km and
uses the Haversine formula to compute the shortest distance over the
earth's surface.
Parameters
----------
lat1 : float
Latitude of the first point in decimal degrees.
lon1 : float
Longitude of the first point in decimal degrees.
lat2 : float
Latitude of the second point in decimal degrees.
lon2 : float
Longitude of the second point in decimal degrees.
Returns
-------
float
The distance between the two points in kilometers.
Examples
--------
>>> haversine(47.37, 8.54, 46.20, 6.14)
224.21
"""
# (Math implementation goes here...)
pass
A professional docstring typically contains:
A Brief Summary: One or two sentences explaining the tool’s purpose.
Parameters: A list of the expected arguments, including their data types (e.g.,
float,list,str) and any default values.Returns: An explanation of the data that the function hands back to the program.
Examples (Optional): Quick usage examples demonstrating how to call the function.
2. Introspection with help()¶
Why go through the effort of writing docstrings instead of just using regular # comments?
Because Python actively reads docstrings and makes them available to the user interactively. This is known as introspection. If you forget how your own function works, or if you are using a function written by a colleague, you do not need to open their source code to read it.
You can simply pass the function’s name into Python’s built-in help() function:
# Ask Python how the function works
help(haversine)
Help on function haversine in module __main__:
haversine(lat1, lon1, lat2, lon2)
Calculates the great-circle distance between two geographic coordinates.
This function assumes a spherical Earth with a radius of 6371.0 km and
uses the Haversine formula to compute the shortest distance over the
earth's surface.
Parameters
----------
lat1 : float
Latitude of the first point in decimal degrees.
lon1 : float
Longitude of the first point in decimal degrees.
lat2 : float
Latitude of the second point in decimal degrees.
lon2 : float
Longitude of the second point in decimal degrees.
Returns
-------
float
The distance between the two points in kilometers.
Examples
--------
>>> haversine(47.37, 8.54, 46.20, 6.14)
224.21
Output:
Help on function haversine in module __main__:
haversine(lat1, lon1, lat2, lon2)
Calculates the great-circle distance between two geographic coordinates.
This function assumes a spherical Earth with a radius of 6371.0 km and
uses the Haversine formula to compute the shortest distance over the
earth's surface.
Parameters
----------
lat1 : float
Latitude of the first point in decimal degrees.
...
Furthermore, modern Integrated Development Environments (IDEs) like VS Code or PyCharm automatically read these docstrings. When you start typing haversine( in your editor, a tooltip will pop up displaying your documentation, helping you remember the correct order of the latitudes and longitudes!
Concept Check: The Instant Manual¶
You are reviewing a colleague’s script and see a custom function called calculate_terrain_ruggedness(). You need to know what parameters it expects. What is the fastest, most Pythonic way to find this information without opening their original source code file?
A) Print the function directly: print(calculate_terrain_ruggedness)
B) Call the function with empty parentheses to read the error message.
C) Use introspection: help(calculate_terrain_ruggedness)
Check your understanding
Answer: C
Passing the function name (without parentheses) into Python’s built-in help() function will instantly display its docstring, including the summary, parameters, and returns. This process, known as introspection, is exactly why writing professional docstrings is so valuable to your future self and your collaborators.
3. The Common Mistakes Checklist¶
Before you move on to the exercises, review this checklist. These are the most frequent, high-impact bugs that catch beginners off guard when building functions.
4. Exercises¶
Put your professional coding skills to the test by documenting and debugging these spatial tools!
Exercise 1: The Documentation Intern (Core)¶
You have been handed a poorly documented tool written by a former colleague. It converts a speed from knots (nautical miles per hour) to kilometers per hour, which is useful for tracking marine wildlife.
Your Task: Write a complete, professional NumPy-style docstring for this function. Ensure you include a brief description, the Parameters, the Returns, and at least one Examples block.
(Hint: 1 knot = 1.852 km/h)
def knots_to_kmh(speed_knots):
return speed_knots * 1.852
Sample solution
def knots_to_kmh(speed_knots):
"""
Converts a speed from knots to kilometers per hour (km/h).
Parameters
----------
speed_knots : float
The speed of the object in knots (nautical miles per hour).
Returns
-------
float
The converted speed in kilometers per hour.
Examples
--------
>>> knots_to_kmh(10)
18.52
"""
return speed_knots * 1.852
# You can now test it with help(knots_to_kmh)Exercise 2: The Bug Hunt (Stretch)¶
The following script contains three different spatial functions that try to calculate the area of a square parcel of land based on side length (L). However, they each break one rule from our Common Mistakes Checklist.
Your Task:
Read the code and identify which specific mistake each function makes.
Fix all three functions so they work correctly and safely.
# Function A
def get_area_a(L):
area = L * L
# Function B
def get_area_b(L, factor=1.0, precision):
return (L * L * factor)
# Function C
global_side_length = 50
def get_area_c():
return global_side_length * global_side_length
Sample solution
The Mistakes:
Function A forgets the
returnstatement. It will run, butareawill vanish, returningNone.Function B places a required parameter (
precision) after an optional default parameter (factor=1.0). Python will throw aSyntaxError.Function C uses a global variable instead of taking an input parameter. If someone changes
global_side_lengthelsewhere in the script, this tool breaks.
The Fixes:
# Fixed A: Add return
def get_area_a(L):
area = L * L
return area
# Fixed B: Reorder parameters (required first)
def get_area_b(L, precision, factor=1.0):
return (L * L * factor)
# Fixed C: Pass as a parameter
def get_area_c(side_length):
return side_length * side_lengthExercise 3: The Refactoring Challenge (Challenge)¶
A researcher wrote a quick script to track the GPS coordinates of a tagged wolf pack over time. Every time the pack moves, they want to log the new coordinates to a list.
Look at their code below. It uses the dangerous “Mutable Default Trap,” and it prints its result instead of returning it.
Your Task: 1. Refactor (rewrite) the function log_movement so it is safe.
2. Use None for the default track parameter, initializing the empty list properly inside the function block.
3. Change the function so it returns the updated list instead of printing it.
4. Add a basic NumPy-style docstring explaining what the function does.
# The Dangerous Script
def log_movement(coord, track=[]):
track.append(coord)
print("New track logged:", track)
# Tracking the wolf pack
log_movement([46.770343, 8.886892])
log_movement([46.770352, 8.886903])
New track logged: [[46.770343, 8.886892]]
New track logged: [[46.770343, 8.886892], [46.770352, 8.886903]]
Sample solution
def log_movement(coord, track=None):
"""
Logs a new geographic coordinate to an existing track list.
Parameters
----------
coord : list
A two-item list containing the [lat, lon] of the new movement.
track : list, optional
An existing list of coordinates to append to. Defaults to None,
which initializes a brand new, empty track list.
Returns
-------
list
The updated track list containing the new coordinate.
"""
# Fix the mutable default trap
if track is None:
track = []
track.append(coord)
# Fix the missing return statement
return track
# Safely testing the tracking tool
wolf_track = log_movement([46.770343, 8.886892])
wolf_track = log_movement([46.770352, 8.886903], track=wolf_track)
print(f"Final safe track: {wolf_track}")5. Summary¶
Congratulations! You have mastered the core mechanics of functional programming.
You now know how to:
Package repeatable spatial math into modular
defblocks.Safely manage variable scope and memory.
Handle unpredictable data streams using
*argsandkwargs.Document your tools using professional, reproducible NumPy-style docstrings.
What comes next?¶
In the upcoming practical exercises, you will:
build reusable spatial tools by turning mathematical logic into well-structured functions
compare geometric models programmatically using nested loops and formatted output
design flexible, professional tools using
*args,kwargs, and NumPy-style docstrings