Friday Finking: Docstrings and DataClasses

dn PythonList at DancesWithMice.info
Sat Nov 27 01:15:55 EST 2021


How have you updated your (team's) standards and conventions for
docstrings, when using dataclasses?


NB the question is specifically class-related. Whereas many of the
examples 'here' are of functions; a docstring is a docstring, is a
docstring.


The original word on the subject is/was "PEP 257 -- Docstring
Conventions" (https://www.python.org/dev/peps/pep-0257/) and included
suggestions such as:

def complex(real=0.0, imag=0.0):
    """Form a complex number.

    Keyword arguments:
    real -- the real part (default 0.0)
    imag -- the imaginary part (default 0.0)
    """
    if imag == 0.0 and real == 0.0:
        return complex_zero
    ...

The key information is the purpose of the function. We also find a
re-statement of the parameter names, their default-values, an intimation
of their types, and an indication of their purpose.

NB no mention of the output/return-value's specifics.


This format plays-nicely with the Python help() system and related.


Barely one year later the topic was advanced, with the more detailed
"PEP 287 -- reStructuredText Docstring Format"
(https://www.python.org/dev/peps/pep-0287/).

This idea is better-illustrated in "The Sphinx docstring format"
(https://sphinx-rtd-tutorial.readthedocs.io/en/latest/docstrings.html).

That offers a template:

"""[Summary]

:param [ParamName]: [ParamDescription], defaults to [DefaultParamVal]
:type [ParamName]: [ParamType](, optional)
...
:raises [ErrorType]: [ErrorDescription]
...
:return: [ReturnDescription]
:rtype: [ReturnType]
"""

...with practical-example docstrings written for "the SimpleBleDevice
class, which is defined within our simpleble module":

class SimpleBleDevice(object):
    """This is a conceptual class representation of a simple BLE device
    (GATT Server). It is essentially an extended combination of the
    :class:`bluepy.btle.Peripheral` and :class:`bluepy.btle.ScanEntry`
classes

    :param client: A handle to the :class:`simpleble.SimpleBleClient` client
        object that detected the device
    :type client: class:`simpleble.SimpleBleClient`
    :param addr: Device MAC address, defaults to None
    :type addr: str, optional
    :param addrType: Device address type - one of ADDR_TYPE_PUBLIC or
        ADDR_TYPE_RANDOM, defaults to ADDR_TYPE_PUBLIC
    :type addrType: str, optional
    :param iface: Bluetooth interface number (0 = /dev/hci0) used for the
        connection, defaults to 0
    :type iface: int, optional
    :param data: A list of tuples (adtype, description, value)
containing the
        AD type code, human-readable description and value for all available
        advertising data items, defaults to None
    :type data: list, optional
    :param rssi: Received Signal Strength Indication for the last received
        broadcast from the device. This is an integer value measured in dB,
        where 0 dB is the maximum (theoretical) signal strength, and more
        negative numbers indicate a weaker signal, defaults to 0
    :type rssi: int, optional
    :param connectable: `True` if the device supports connections, and
`False`
        otherwise (typically used for advertising ‘beacons’).,
        defaults to `False`
    :type connectable: bool, optional
    :param updateCount: Integer count of the number of advertising packets
        received from the device so far, defaults to 0
    :type updateCount: int, optional
    """

(apologies: email word-wrap likely wrecks their neat formatting)


We've probably reached the point-in-time when this 'grumpy old man'
bemoans that such formulaic approaches tend to lead to slavish habits -
which become vices when they do nothing to improve the intended
objectives of "readability" (or worse detract from it), ie the docstring
equivalent of:

counter += 1  # add one to counter


Management advice: if you set a standard/requirement, such will be met -
without regard for other measures of quality, ie be careful what you
wish for!


Why the negative attitude? An example published in "Python Docstrings :
How to document your Python code ?"
(https://amiradata.com/python-docstrings/):

def setType(self,type):
    """Change the pokemon type.

        The parameter value is stored in the type
        variable of the pokemon class.

        :param type: type pokemon
        :type type: string
        :return: no value
        :rtype: none
        """

    self.type = type
    return

    return self.type

NB are you also amused by the inclusion of a question-mark in the
article's title?

The use of the name "type", alongside the Python term "type", adds to my
confusion (because I didn't/don't even know that there is more than one
type/kind/flavor of Pokemon (mea culpa!)). To say nothing of the
multiple return statements and non-PEP-008 naming.


Fast-forward a good 15 years and today we have 'typing' (many PEP-refs)
and "PEP 557 -- Data Classes"
(https://www.python.org/dev/peps/pep-0557/), which combined to become
the "Data Classes" library in the PSL
(https://docs.python.org/3/library/dataclasses.html)

Now we can write:

@dataclass
class InventoryItem:
    """Class for keeping track of an item in inventory."""
    name: str
    unit_price: float
    quantity_on_hand: int = 0

The docstring itself still covers the purpose of the function.

In this case, there is no re-statement of the parameter names (which
would otherwise have appeared in __init__()), because their declaration
is immediately beneath the docstring.

As are any default-values.

The parameter-typing has become a compulsory part of the code (cf
added-documentation).

All we appear to be missing is an indication of each parameter's purpose
- over-and-above any meaning which can be gleaned from the careful
choice of attribute-name.

Plus, if we add return-value typing to a function def, then that need is
similarly satisfied.

Yes, I'm a strong user of typing (but not always)...


Finally, to the strong-arm tactics that linters seem to have become:

I'm amused (ok, it irritates) that one of the linters built-in or
plugged-into my IDE does not like in-line comments. No place, no time!@
Accordingly, the following results in criticism:

    unit_price: float  # the cost of a single unit in NZD
    quantity_on_hand: int = 0  # excludes items reserved by Sales Dept

Yet, such seems a quite-reasonable (updated for dataclasses) approach.


Do you agree?

Do you not bother with docstring style at all?

Have you stuck with PEP-257, after all these years?

Do you (still) use the Sphinx/RST format for docstrings, even though it
seems even more repetitive and wordy?

Have you updated your conventions/style-manual to acknowledge
dataclasses? If so, how?
-- 
Regards,
=dn


More information about the Python-list mailing list