SECTION 1
Historical Context & Motivation
From the very first days of computing, programmers realized that writing code is only half the battle — someone else (or even your future self) has to read that code later. Early programming languages like FORTRAN (1957) allowed programmers to insert comment lines so that human readers could understand what the machine instructions were doing. Without comments, revisiting a program after even a few weeks felt like deciphering a secret language.
As software projects grew from hundreds to millions of lines, the need for structured, machine-readable documentation became urgent. Languages began introducing docstrings — special strings embedded directly in source code that tools could extract automatically. Python, created by Guido van Rossum in 1991, embraced this idea early on, making docstrings a first-class feature of the language. Today, every serious Python project relies on both comments and docstrings to keep code understandable and maintainable.
1957 • FORTRAN Comment Lines
C character in column 1 to mark a line as a comment, establishing the tradition of inline documentation.1991 • Python Is Born
# symbol for comments and triple-quoted strings that will later become docstrings.2001 • PEP 257 — Docstring Conventions
2010s • Auto-Doc Tools Mature
2020s • AI-Powered Code Assistants
So here is the central question this lesson addresses: how do you write comments and docstrings that are genuinely meaningful — adding clarity without cluttering your code? Let's find out.
SECTION 2
Core Principles & Definitions
Before diving into syntax, it helps to understand the two distinct tools Python gives you for documenting code. A comment is a note for humans that Python ignores entirely — it starts with # and extends to the end of the line. A docstring is a special string literal placed as the very first statement inside a module, class, method, or function. Unlike comments, Python actually stores docstrings so that tools and the built-in help() function can access them at runtime.
Comments Explain "Why"
Docstrings Explain "What" & "How"
Don't Repeat the Code
# add 1 to x above x += 1 wastes the reader's time.Keep It Current
Follow PEP 257
KEY TAKEAWAY
SECTION 3
Visual Explanation — Anatomy of a Documented Function
The diagram below breaks down a well-documented Python function, labeling every part so you can see exactly where comments and docstrings belong and how they differ in purpose.
Look at how each piece of documentation has a clear job. The green comment at the top gives context about the project (a weather app). The violet docstring follows PEP 257 format: a one-line summary, then sections for Args and Returns. The second green comment clarifies the math formula for anyone skimming the code quickly. Together, they make the function self-explanatory.
SECTION 4
How Comments & Docstrings Work Under the Hood
Understanding the difference between comments and docstrings becomes easier when you know what Python actually does with each one. A comment is stripped away by the Python interpreter before any code runs. It is truly invisible to the program. A docstring, on the other hand, is stored as an attribute called __doc__ on the object it belongs to. This means you can access it at runtime, which is what help() and documentation generators rely on.
Comment Syntax
In Python, everything from the # symbol to the end of that line is a comment. Python does not have a built-in multi-line comment syntax like some languages (e.g., /* ... */ in Java). To write multi-line comments, you simply put # at the start of each line.
Docstring Syntax
A docstring uses triple quotes — either """triple double quotes""" or '''triple single quotes'''. By convention, double quotes are preferred. The docstring must be the very first statement inside a function, class, or module for Python to recognize it as the __doc__ attribute. If you place it anywhere else, it is just a regular string that gets ignored.
Accessing Docstrings at Runtime
You can read any object's docstring using print(my_function.__doc__) or by calling help(my_function) in the Python interactive shell. This is incredibly useful — imagine being able to look up a recipe just by asking the oven what it can cook. That is effectively what docstrings give you: built-in, always-available instructions.
__doc__ attribute and can be retrieved with help().SECTION 5
Types of Documentation & Style Conventions
Not all comments and docstrings are created equal. Understanding the different types helps you choose the right tool for each situation. Let's classify them and compare the most popular docstring formats.
Types of Comments
| Type | Example | When to Use |
|---|---|---|
| Block comment | # Retry up to 3 times because the API is flaky | Before a block of code to explain a non-obvious decision |
| Inline comment | x = x + 1 # compensate for 0-based index | After a line to clarify a single tricky expression |
| TODO comment | # TODO: add input validation | To mark planned improvements that you haven't implemented yet |
Common comment types in Python
Docstring Formats Compared
Several popular docstring styles exist. The three most common are Google style, NumPy/SciPy style, and reStructuredText (reST) style. Pick one style and use it consistently throughout your project.
| Feature | Google Style | NumPy Style |
|---|---|---|
| Parameters heading | Args: | Parameters
---------- |
| Return heading | Returns: | Returns
------- |
| Readability | Compact and clean — great for short functions | Spacious — ideal for complex scientific functions |
| Common in | General Python, web apps, startups | Data science, machine learning libraries |
Google vs. NumPy docstring styles
Pro Tip
Args:, Returns:, and Raises: followed by indented descriptions.SECTION 6
Worked Example — Documenting a Student Gradebook Function
Let's walk through the process of writing documentation for a function that calculates a student's letter grade. We will start with undocumented code and transform it step by step into a well-documented piece of software.
Documenting calculate_grade()
- 1
Step 1 — Start With the Bare Function
Here is the raw function with no documentation at all:def calculate_grade(scores): avg = sum(scores) / len(scores) if avg >= 90: return 'A' elif avg >= 80: return 'B' elif avg >= 70: return 'C' elif avg >= 60: return 'D' else: return 'F'This works, but anyone reading it has to figure out whatscoresshould be, what the function returns, and what scale it uses. - 2
Step 2 — Add the Docstring Summary Line
The first line of a docstring should be a brief, complete sentence that summarizes the function's purpose. It should fit on one line."""Calculate a letter grade from a list of numeric scores."""Summary: one sentence describing what the function does. - 3
Step 3 — Add Args and Returns Sections
Next, list each parameter with its expected type and a short description. Then describe the return value."""Calculate a letter grade from a list of numeric scores. Args: scores (list[float]): A list of numeric test scores (each between 0 and 100). Returns: str: A single letter grade ('A', 'B', 'C', 'D', or 'F'). """Args and Returns sections now tell the user exactly what to pass in and what to expect back. - 4
Step 4 — Add a Clarifying Comment for the Grading Scale
The grading thresholds are a design decision. A comment explains why these particular numbers were chosen.# Using standard 10-point grading scale (A=90+, B=80+, etc.)The comment justifies the choice of thresholds without repeating the code. - 5
Step 5 — Final Documented Function
Putting it all together:def calculate_grade(scores): """Calculate a letter grade from a list of numeric scores. Args: scores (list[float]): A list of numeric test scores (each between 0 and 100). Returns: str: A single letter grade ('A', 'B', 'C', 'D', or 'F'). """ avg = sum(scores) / len(scores) # Using standard 10-point grading scale (A=90+, B=80+, etc.) if avg >= 90: return 'A' elif avg >= 80: return 'B' elif avg >= 70: return 'C' elif avg >= 60: return 'D' else: return 'F'Now anyone can callhelp(calculate_grade)and immediately know what it does, what it needs, and what it returns.
SECTION 7
Good vs. Bad Documentation — Strengths & Pitfalls
Writing documentation is a skill, and like any skill, there are common mistakes. The table below contrasts effective documentation with problematic documentation so you can recognize the difference instantly.
| ❌ Bad Practice | ✅ Good Practice | Why It Matters |
|---|---|---|
# increment x
x += 1 | # Off-by-one fix: API uses 1-based indexing
x += 1 | The bad comment restates the code. The good comment explains the reason. |
def f(a, b):
"""Function.""" | def add_scores(current, new):
"""Add new scores to the current total.""" | Vague names and docstrings force the reader to read every line of code. |
| No docstring at all on a public function | At minimum, a one-line docstring summarizing purpose | Without a docstring, help() returns nothing useful. |
| Outdated comment that contradicts the code | Updated comment that matches current behavior | Misleading docs create bugs and waste debugging time. |
| Commenting every single line | Commenting only where intent is non-obvious | Over-commenting clutters the code and makes real comments hard to spot. |
Common documentation pitfalls and their fixes
KEY TAKEAWAY
SECTION 8
Connection to Advanced Practices
Once you're comfortable writing comments and docstrings, you'll encounter more advanced documentation practices in professional Python development. These build directly on what you've learned.
| What You Know Now | What Comes Next | How They Connect |
|---|---|---|
| Describing parameter types in docstrings | Type hints — def greet(name: str) -> str: | Type hints formalize what docstrings describe informally, letting tools catch errors automatically. |
| Writing one-line summaries of functions | Sphinx auto-documentation | Sphinx reads your docstrings and generates a full website of documentation — the better your docstrings, the better the website. |
| Using examples in docstrings | Doctests — >>> add(2, 3)
5 | Python's doctest module can run the examples in your docstrings as actual tests, ensuring your docs stay accurate. |
| Commenting "why" for tricky code | Architecture Decision Records (ADRs) | In large projects, teams write formal documents explaining major design decisions — like comments, but at a project level. |
From basics to advanced documentation practices
As you move into AP Computer Science, college courses, or open-source projects, these advanced techniques will feel like natural extensions of the habits you're building now. Every professional Python developer writes docstrings — the skill you're learning today is one you'll use for the rest of your programming career.
SECTION 9
Practice Problems
Question 1: CONCEPTUAL
Show answer
#) is completely ignored by the Python interpreter — it is stripped away during parsing and never becomes part of the running program. A docstring (a triple-quoted string placed as the first statement in a function, class, or module) is stored by Python as the __doc__ attribute of that object. This means docstrings can be accessed at runtime using help() or .__doc__, while comments cannot. In short, comments are for developers reading the source file; docstrings are for anyone using the code, even without opening the file.Question 2: BASIC
def area_of_circle(radius):
return 3.14159 * radius ** 2Show answer
def area_of_circle(radius):
"""Calculate the area of a circle given its radius.
Args:
radius (float): The radius of the circle (must be non-negative).
Returns:
float: The area of the circle, calculated as π × r².
"""
return 3.14159 * radius ** 2
The summary line states what the function does. The Args section specifies the parameter name, its expected type, and a brief note about valid values. The Returns section describes the output type and the formula used.Question 3: INTERMEDIATE
# This function adds two numbers
def calculate_bmi(weight_kg, height_m):
# calculate bmi
bmi = weight_kg / (height_m ** 2)
# WHO classifies BMI >= 30 as obese
if bmi >= 30:
return 'obese'
return 'not obese'Show answer
# This function adds two numbers) is misleading — the function calculates BMI, not addition. This is a classic example of an outdated comment that was never updated. It should be removed or replaced with a proper docstring. Comment 2 (# calculate bmi) is redundant — the variable name bmi and the formula make this obvious. Remove it. Comment 3 (# WHO classifies BMI >= 30 as obese) is helpful — it explains why the threshold of 30 was chosen by citing the WHO standard. Keep it.Question 4: APPLIED
is_overdue that takes a due_date (string in 'YYYY-MM-DD' format) and returns True if today's date is past the due date. Include a docstring with Args, Returns, and an Example section, plus at least one meaningful comment in the code body.Show answer
from datetime import date
def is_overdue(due_date):
"""Check whether a library book is overdue.
Args:
due_date (str): The due date in 'YYYY-MM-DD' format.
Returns:
bool: True if today is past the due date, False otherwise.
Example:
>>> is_overdue('2020-01-01')
True
"""
# Convert string to date object for reliable comparison
deadline = date.fromisoformat(due_date)
return date.today() > deadline
The docstring covers what the function does, what it expects, what it returns, and provides a usage example. The inline comment explains why we convert the string — without it, a reader might wonder why we don't just compare strings directly (string comparison would fail for dates like '2023-9-5' vs '2023-10-1').Question 5: CRITICAL THINKING
Show answer
total_price = base_price * (1 + tax_rate) needs no comment at all. However, docstrings and comments still matter in several situations. First, public APIs: when someone imports your function, they can't see your variable names without opening the file. A docstring lets them call help() and learn the function's contract instantly. Second, non-obvious business rules: code can show that you check age >= 13, but only a comment can explain why — perhaps COPPA regulations require it. Conversely, a simple getter function like def get_name(self): return self.name probably doesn't need a comment beyond a minimal docstring, because the code is truly self-explanatory. The bottom line: clean code eliminates bad comments, but it doesn't eliminate the need for good ones.SUMMARY
Lesson Summary
Summary
Python gives you two tools for documentation: comments (lines starting with #) and docstrings (triple-quoted strings placed as the first statement in a function, class, or module). Comments are discarded by the interpreter and exist only in source code, while docstrings are stored at runtime as the __doc__ attribute and accessible via help(). Comments should explain why code works a certain way, while docstrings describe what a function does and how to use it.
Follow PEP 257 conventions and choose a consistent style like Google style with Args: and Returns: sections. Avoid the three big pitfalls: restating obvious code, leaving docs outdated, and over-commenting every line. As you advance, docstrings connect directly to type hints, auto-documentation tools, and doctests — making today's habit one of the most valuable skills in professional Python development.