In-code documentation
Questions
What can I do to make my code more easily understandable?
What information should go into comments?
What are docstrings and what information should go into docstrings?
In this episode we will learn how to write good documentation inside your code.
Exercise - Writing good comments
Sometimes version control is better than a comment
Examples for code comments where Git is a better solution
Keeping zombie code “just in case” (rather use version control):
# do not run this code!
# if temperature > 0:
# print("It is warm")
Instead: Remove the code, you can always find it back in a previous version of your code in Git.
Emulating version control:
# John Doe: threshold changed from 0 to 15 on August 5, 2013
if temperature > 15:
print("It is warm")
Instead: You can get this information from git log
or git show
or git annotate
or similar.
What are “docstrings” and how can they be useful?
Here is function fahrenheit_to_celsius
which converts temperature in
Fahrenheit to Celsius, implemented in a couple of different languages.
Your language is missing? Please contribute an example.
The first set of examples uses regular comments:
# This function converts a temperature in Fahrenheit to Celsius.
def fahrenheit_to_celsius(temp_f: float) -> float:
temp_c = (temp_f - 32.0) * (5.0/9.0)
return temp_c
# Convert Fahrenheit to Celsius
fahrenheit_to_celsius <- function(temp_f)
{
temp_c <- (temp_f - 32.0) * (5.0/9.0)
temp_c
}
# This function converts a temperature in Fahrenheit to Celsius.
function fahrenheit_to_celsius(temp_f)
temp_c = (temp_f - 32.0) * (5.0/9.0)
return temp_c
end
! Convert Fahrenheit to Celsius
function fahrenheit_to_celsius(temp_f) result(temp_c)
implicit none
real temp_f
real temp_c
temp_c = (temp_f - 32.0) * (5.0/9.0)
end function
// Converts a temperature in Fahrenheit to Celsius
double fahrenheit_to_celsius(double temp_f) {
auto temp_c = (temp_f - 32.0) * (5.0 / 9.0);
return temp_c;
}
// Convert Fahrenheit to Celsius
fn fahrenheit_to_celsius(temp_f: f64) -> f64 {
let temp_c = (temp_f - 32.0) * 5.0 / 9.0
temp_c
}
The second set uses docstrings or similar concepts. Please compare the two (above and below):
def fahrenheit_to_celsius(temp_f: float) -> float:
"""
Converts a temperature in Fahrenheit to Celsius.
Parameters
----------
temp_f : float
The temperature in Fahrenheit.
Returns
-------
float
The temperature in Celsius.
"""
temp_c = (temp_f - 32.0) * (5.0/9.0)
return temp_c
Read more: https://peps.python.org/pep-0257/
#' Convert Fahrenheit to Celsius
#'
#' @param temp_f A numeric vector of temperatures in Fahrenheit.
#' @return A numeric vector of temperatures in Celsius.
fahrenheit_to_celsius <- function(temp_f)
{
temp_c <- (temp_f - 32.0) * (5.0/9.0)
temp_c
}
Read more: https://cran.r-project.org/web/packages/roxygen2/vignettes/roxygen2.html
"""
fahrenheit_to_celsius(temp_f::Float)
Converts temperature in Fahrenheit to Celsius.
# Arguments
- `temp_f::Float`: Temperature in Fahrenheit.
# Returns
- `temp_c::Float`: Temperature in Celsius.
"""
function fahrenheit_to_celsius(temp_f)
temp_c = (temp_f - 32.0) * (5.0/9.0)
return temp_c
end
Read more: https://docs.julialang.org/en/v1/manual/documentation/
!> @brief Convert Fahrenheit to Celsius
!! @param temp_f Temperature in Fahrenheit
!! @return Temperature in Celsius
function fahrenheit_to_celsius(temp_f) result(temp_c)
implicit none
real temp_f
real temp_c
temp_c = (temp_f - 32.0) * (5.0/9.0)
end function
Read more: https://en.wikibooks.org/wiki/Fortran/Documenting_Fortran
// @brief: Converts a temperature in Fahrenheit to Celsius
//
// @param: temp_f: Temperature in Fahrenheit
//
// @return: Temperature in Celsius
double fahrenheit_to_celsius(double temp_f) {
auto temp_c = (temp_f - 32.0) * (5.0 / 9.0);
return temp_c;
}
Read more: https://www.doxygen.nl
/// Convert Fahrenheit to Celsius
/// # Arguments
/// * `temp_f` - Temperature in Fahrenheit
///
/// # Returns
/// * `temp_c` - Temperature in Celsius
fn fahrenheit_to_celsius(temp_f: f64) -> f64 {
let temp_c = (temp_f - 32.0) * 5.0 / 9.0
temp_c
}
Read more: https://doc.rust-lang.org/rust-by-example/meta/doc.html
Docstrings can do a bit more than just comments:
Tools can generate help text automatically from the docstrings.
Tools can generate documentation pages automatically from code.
It is common to write docstrings for functions, classes, and modules.
Good docstrings describe:
What the function does
What goes in (including the type of the input variables)
What goes out (including the return type)
Naming is documentation: Giving explicit, descriptive names to your code segments (functions, classes, variables) already provides very useful and important documentation. In practice you will find that for simple functions it is unnecessary to add a docstring when the function name and variable names already give enough information.
Keypoints
Comments should describe the why for your code not the what.
Writing docstrings can be a good way to write documentation while you type code since it also makes it possible to query that information from outside the code or to auto-generate documentation pages.
In-code-1: Comments
Let’s take a look at two example comments (comments in Python start with
#
):Comment A
Comment B
Which of these comments is more useful? Can you explain why?
Solution
Comment A describes what happens in this piece of code. This can be useful for somebody who has never seen Python or a program, but for somebody who has, it can feel like a redundant commentary.
Comment B is probably more useful as it describes why this piece of code is there, i.e. its purpose.