Documenting code#
In this lecture, we are going to cover some basic recommendations and good practices for documenting your code.
2. Docstrings#
We can add comments to any function, class or module in Python using the following syntax.
def sq(x):
"""
Returns the square of a number.
"""
return x**2
And then we can ask for the current documentation of such module with the ?
special character:
sq?
Signature: sq(x)
Docstring: Returns the square of a number.
File: /tmp/ipykernel_296/3143057924.py
Type: function
The same character ?
also works for objects imported from libraries:
import numpy as np
np.linspace?
Signature:
np.linspace(
start,
stop,
num=50,
endpoint=True,
retstep=False,
dtype=None,
axis=0,
)
Docstring:
Return evenly spaced numbers over a specified interval.
Returns `num` evenly spaced samples, calculated over the
interval [`start`, `stop`].
The endpoint of the interval can optionally be excluded.
.. versionchanged:: 1.16.0
Non-scalar `start` and `stop` are now supported.
.. versionchanged:: 1.20.0
Values are rounded towards ``-inf`` instead of ``0`` when an
integer ``dtype`` is specified. The old behavior can
still be obtained with ``np.linspace(start, stop, num).astype(int)``
Parameters
----------
start : array_like
The starting value of the sequence.
stop : array_like
The end value of the sequence, unless `endpoint` is set to False.
In that case, the sequence consists of all but the last of ``num + 1``
evenly spaced samples, so that `stop` is excluded. Note that the step
size changes when `endpoint` is False.
num : int, optional
Number of samples to generate. Default is 50. Must be non-negative.
endpoint : bool, optional
If True, `stop` is the last sample. Otherwise, it is not included.
Default is True.
retstep : bool, optional
If True, return (`samples`, `step`), where `step` is the spacing
between samples.
dtype : dtype, optional
The type of the output array. If `dtype` is not given, the data type
is inferred from `start` and `stop`. The inferred dtype will never be
an integer; `float` is chosen even if the arguments would produce an
array of integers.
.. versionadded:: 1.9.0
axis : int, optional
The axis in the result to store the samples. Relevant only if start
or stop are array-like. By default (0), the samples will be along a
new axis inserted at the beginning. Use -1 to get an axis at the end.
.. versionadded:: 1.16.0
Returns
-------
samples : ndarray
There are `num` equally spaced samples in the closed interval
``[start, stop]`` or the half-open interval ``[start, stop)``
(depending on whether `endpoint` is True or False).
step : float, optional
Only returned if `retstep` is True
Size of spacing between samples.
See Also
--------
arange : Similar to `linspace`, but uses a step size (instead of the
number of samples).
geomspace : Similar to `linspace`, but with numbers spaced evenly on a log
scale (a geometric progression).
logspace : Similar to `geomspace`, but with the end points specified as
logarithms.
Examples
--------
>>> np.linspace(2.0, 3.0, num=5)
array([2. , 2.25, 2.5 , 2.75, 3. ])
>>> np.linspace(2.0, 3.0, num=5, endpoint=False)
array([2. , 2.2, 2.4, 2.6, 2.8])
>>> np.linspace(2.0, 3.0, num=5, retstep=True)
(array([2. , 2.25, 2.5 , 2.75, 3. ]), 0.25)
Graphical illustration:
>>> import matplotlib.pyplot as plt
>>> N = 8
>>> y = np.zeros(N)
>>> x1 = np.linspace(0, 10, N, endpoint=True)
>>> x2 = np.linspace(0, 10, N, endpoint=False)
>>> plt.plot(x1, y, 'o')
[<matplotlib.lines.Line2D object at 0x...>]
>>> plt.plot(x2, y + 0.5, 'o')
[<matplotlib.lines.Line2D object at 0x...>]
>>> plt.ylim([-0.5, 1])
(-0.5, 1)
>>> plt.show()
File: /srv/conda/envs/notebook/lib/python3.10/site-packages/numpy/core/function_base.py
Type: function
Note
Notice that ?
prints just the docstring, while ??
shows the docstring and the source code.
Now, there is a convetion of how to write good dosctring Numpy docstring standard.
The docstring included in the function declaration is the same one than it is later rendered online to create the Numpy documentation. See for example the documentation of np.linspace
and see it matches what we just printed.
The same logic applies when we write modules. For example, we can create a module with a docstring on top of mymod.py
script:
"""
My module - utility functions
...
"""
def sq(x):
return x**2
and then we can just import the module and see this
import mymod
mymod?
Note
If it worth writing a function, then it is worth trying to write a small docstring. This doesn’t mean we need to write a long docstring following all the conventions, we can do this later.
Investment of time: how much time we may spend figuring out what the code is doing later vs the price of writing a small comment in the code
3. Stile Guide#
If well writing elegant code is not part of the documentation, it definitively serves as a tool to create more readable code. The are different standards of how to write good code, that include rules syntax (spaces, comments, parenthesis) and semantics of code (nomenclature for different types). The PEP 8 -Style Guide for Python Code includes many of this standards.
1. Comments#
It is useful to add comments as we make progress in our code. However, it is non-trivial to trade-off a code with non-comments and an over-commented code. For example, we definitively don’t want to overload our code with useless comments like
Tip
A good question to ask to know if you have enough comments in your code is If I stop coding now and I closed my computer, would I be able to understand what I was doing later with the code as it is?.
Sometimes a specific piece of code, like a line, requires extra explanation to understand the logic of what we are doing. This could include a reference to a mathematical identity or link to a place where that is explained.
Another recommendation is in general to comment of sections of code, for example, in functions. If a piece of code is worth enough to put it in a small function or block of code (eg, in a Python class), then it is worth documenting too.
Now, comments are only accessible when we access the source code. In iPython, we can access the source code of a given function with the
??
symbol. For example:Here we can see some comments in the source code. An extra element of documentation we can see here is the header just immediately after the definition of the function. This is called the docstring and it serves as an extra piece of documentation for objects in Python.