Статьи
November 21, 2022

Докстринги: их типы и форматы

Неважно, насколько хороша ваша программа, потому что если документация недостаточно хороша, люди не будут ее использовать.
- Даниэле Прочида

Давайте уже согласимся с Гидо ван Россумом в том, что код чаще читают, чем пишут. А теперь представьте себе ситуацию, когда вы залезли в чужой и страшный скрипт, который не содержит в себе вообще никаких пояснений. Даже если код не чужой, а ваш собственный, но по каким-то причинам успешно забытый, отсутствие хоть какой-то информации о том, что этот код делает, может серьёзно усложнить работу.

Поэтому берём за правило: не пренебрегать документацией.

Что такое докстринги?

Докстринги (они же "строки документации") — встроенные строки, которые, могут помочь людям, работающим в с вашим кодом, и вам самим понять, что в нём происходит.

В Python есть встроенная функция help(), которая выводит документацию различных объектов.

>>> help(str)
Help on class str in module builtins:

class str(object)
 |  str(object='') -> str
 |  str(bytes_or_buffer[, encoding[, errors]]) -> str
 |
 |  Create a new string object from the given object. If encoding or
 |  errors are specified, then the object must expose a data buffer
 |  that will be decoded using the given encoding and error handler.
 |  Otherwise, returns the result of object.__str__() (if defined)
 |  or repr(object).
 |  encoding defaults to sys.getdefaultencoding().
 |  errors defaults or readability

Откуда что берётся? Так как в Python вообще всё — объект, вы можете вывести список с его атрибутами и методами при помощи функции dir().

>>> dir(str)
['__add__', ..., '__doc__', ..., 'zfill'] # Truncated for readability

Если посмотреть, что в находится атрибуте __doc__, то обнаружите следующее:

>>> print(str.__doc__)
str(object='') -> str
str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or
errors are specified, then the object must expose a data buffer
that will be decoded using the given encoding and error handler.
Otherwise, returns the result of object.__str__() (if defined)
or repr(object).
encoding defaults to sys.getdefaultencoding().
errors defaults to 'strict'.

Оппа — мы нашли, где хранятся докстринги. Вы даже можете попробовать их изменить (что, конечно, не сработает со встроенными объектами):

>>> str.__doc__ = "Йа стр0ка"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't set attributes of built-in/extension type 'str'

Зато сработает с кастомными:

def say_hello(name):
    print(f"Привет, {name}, не меня ли ищещь?")

say_hello.__doc__ = "Просто функция, которая здоровается"
>>> help(say_hello)
Help on function say_hello in module __main__:

say_hello(name)
    Просто функция, которая здоровается

Но вообще способ выше — для сильно упоротых, нормальные люди пишут докстринги в теле функции:

def say_hello(name):
    """ Просто функция, которая здоровается """
    print(f"Привет, {name}, не меня ли ищещь?")
>>> help(say_hello)
Help on function say_hello in module __main__:

say_hello(name)
    Просто функция, которая здоровается

Типы докстрингов

Соглашения о докстрингах описаны в PEP 257. Они должны быть достаточно краткими, но при этом достаточно подробными, чтобы новые пользователи могли понять, как использовать документированный объект.

Во всех случаях для документирования должен использоваться формат строки с тремя двойными кавычками (""") независимо от того, является ли текст многострочным или нет. Все строки докстринга, как и в случае с комментариями, не должны превышать длину в 72 символа.

Докстринги можно разделить на 4 основные категории:

  • Докстринги классов
  • Докстринги пакетов и модулей
  • Докстринги скриптов

Докстринги классов

Докстринги класса создаются для самого класса, а также для любых его методов и размещаются непосредственно после объявления класса или метода:

class SimpleClass:
    """Здесь докстринг класса"""

    def say_hello(self, name: str):
        """Здесь докстринг метода класса"""

        print(f"Привет, {name}")

Докстринги классов должны содержать:

  • краткое описание его назначения и поведения;
  • любые публичные методы с кратким описанием;
  • любые свойства класса (атрибуты);
  • всё, что связано с интерфейсом для подклассификаторов, если класс предназначен для подклассификации.

Докстринги методов класса должны содержать:

  • краткое описание того, что представляет собой метод и для чего он используется;
  • любые передаваемые аргументы (как обязательные, так и необязательные), включая аргументы с ключевыми словами;
  • пометку любых аргументов, которые считаются необязательными или имеют значение по умолчанию;
  • любые побочные эффекты, возникающие при выполнении метода;
  • любые возникающие исключения;
  • любые ограничения на вызов метода.

Докстринги пакетов и модулей

Докстринги пакета должны быть размещены в верхней части файла __init__.py. В них должны быть перечислены экспортируемые модули и подпакеты.

Докстринги модулей похожи на докстринги классов. Вместо классов и методов классов документируется модуль и все функции, находящиеся в нём. Документация модуля размещается в верхней части файла, перед любыми импортами. Документация модуля должна включать:

  • краткое описание модуля и его назначения;
  • список всех классов, исключений, функций и любых других объектов, экспортируемых модулем.

Докстринг для функции модуля должен включать те же элементы, что и докстринг метода класса.

Докстринги скриптов

Скрипты — это одиночные исполняемые файлы, запускаемые из консоли. Докстринги для них размещаются в верхней части файла и должны быть документированы достаточно хорошо, чтобы пользователи могли иметь хорошее представление о том, как использовать скрипт.

Любой пользовательский или сторонний импорт должен быть указан в документации, чтобы пользователи могли знать, какие пакеты могут потребоваться для выполнения скрипта. Вот пример скрипта, который используется для простой распечатки заголовков столбцов электронной таблицы:

"""Spreadsheet Column Printer

This script allows the user to print to the console all columns in the
spreadsheet. It is assumed that the first row of the spreadsheet is the
location of the columns.

This tool accepts comma separated value files (.csv) as well as excel
(.xls, .xlsx) files.

This script requires that `pandas` be installed within the Python
environment you are running this script in.

This file can also be imported as a module and contains the following
functions:

    * get_spreadsheet_cols - returns the column headers of the file
    * main - the main function of the script
"""

import argparse

import pandas as pd


def get_spreadsheet_cols(file_loc, print_cols=False):
    """Gets and prints the spreadsheet's header columns

    Parameters
    ----------
    file_loc : str
        The file location of the spreadsheet
    print_cols : bool, optional
        A flag used to print the columns to the console (default is
        False)

    Returns
    -------
    list
        a list of strings used that are the header columns
    """

    file_data = pd.read_excel(file_loc)
    col_headers = list(file_data.columns.values)

    if print_cols:
        print("\n".join(col_headers))

    return col_headers


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument(
        "input_file",
        type=str,
        help="The spreadsheet file to pring the columns of"
    )
    args = parser.parse_args()
    get_spreadsheet_cols(args.input_file, print_cols=True)


if __name__ == "__main__":
    main()

Форматы докстрингов

Существуют специальные форматы докстрингов, которые могут быть использованы, чтобы помочь парсерам и пользователям. Вот некоторые из наиболее распространенных:

Google Dosctring

Этот формат докстрингов рекомендован Khan Academy. Он выглядит следующим образом:

def google_docstrings(num1, num2) -> int:
    """Add up two integer numbers.  
    
    This function simply wraps the ``+`` operator, and does not 
    do anything interesting, except for illustrating what 
    the docstring of a very simple function looks like.  
    
    Args: 
        num1 (int) : First number to add. 
        num2 (int) : Second number to add.  
        
    Returns: 
        The sum of ``num1`` and ``num2``.  
        
    Raises: 
        AnyError: If anything bad happens.  
    """ 
    return num1 + num2

Чтобы упростить себе жизнь, можно установить вот это расширение для VSC. Оно добавляет шаблон для строк документации, нужно будет только описания заполнить.

NumPy Docstring

Этот формат документации используется во многих библиотеках для data science, например, NumPy, SciPy и Pandas. Документация форматируется следующим образом:

def numpy_docstrings(num1, num2) -> int: 
    """ Add up two integer numbers.  
    
    This function simply wraps the ``+`` operator, and does not 
    do anything interesting, except for illustrating what 
    the docstring of a very simple function looks like.  
    
    Parameters 
    ---------- 
    num1 : 
        int First number to add. 
    num2 : int 
        Second number to add.  
    
    Returns 
    ------- 
    int 
        The sum of ``num1`` and ``num2``.  
        
    Raises 
    ====== 
     MyException 
        if anything bad happens  
        
    See Also 
    -------- 
    subtract : Subtract one integer from another.  
    
    Examples 
    -------- 
    >>> add(2, 2) 
    4 
    >>> add(25, 0) 
    25 
    >>> add(10, -10) 
    0 
    """ 
    return num1 + num2
Google Docstrings vs NumPy:

Основное различие между двумя стилями заключается в том, что Google между разделами использует отступы, в то время как NumPy использует подчеркивания. Стиль NumPy, как правило, требует больше пространства по вертикали, в то время как стиль Google будет занимать больше места по горизонтали. Стиль Google легче читается для коротких и простых строк документации, а стиль NumPy — для длинных и подробных строк документации.

Sphinx Docstring

Старый добрый формат sphinx самый простой из всех, но визуально он несколько плотный, что затрудняет его чтение. Выглядит так:

def sphinx_docstrings(num1, num2) -> int:
    """Add up two integer numbers.  
    
    This function simply wraps the ``+`` operator, and does not 
    do anything interesting, except for illustrating what 
    the docstring of a very simple function looks like.  
    
    :param int num1: First number to add. 
    :param int num2: Second number to add. 
    :returns: The sum of ``num1`` and ``num2``. 
    :rtype: int 
    :raises AnyError: If anything bad happens. 
    """ 
    return num1 + num2

Заключение

Кроме перечисленных трёх есть и другие менее распространённые форматы. Тут нет какого-то общепринятого либо объективно лучшего способа, так что выбор за вами.

Выбирайте то, что вам удобно, главное не смешивайте форматы между собой.

А ещё всегда документируйте то, что пишете. Серьёзно, вам же самим потом в этом копаться :)

PythonTalk в Telegram

Чат PythonTalk в Telegram

Предложить материал | Поддержать канал

Источник: Better Programming
Перевод и адаптация: Екатерина Прохорова