PEP 257 на русском. (Соглашение о Docstrings)

Автор оригинала: David Goodger, Guido van Rossum
  • Перевод
Привет, Хабр. Бывают моменты, когда тебе хочется максимально погрузиться в язык и понять все его тонкости. В случае Python один из лучших способов это сделать — читать на официальном сайте документацию и PEP-ы. В своё время я этого не делал, поскольку не мог понять многих «технических» моментов, а вариантов русского перевода не было. Сейчас же я решил сам перевести PEP-257, где рассказывается о правильном документировании кода, ведь наверняка это поможет новичкам лучше понять истинный «пайтоновский» подход к написанию кода. Я переводил примеры кода на русский язык, но только для того, чтобы лучше донести смысл. В реальном программировании старайтесь писать документационные строки на английском. Также говорю сразу, что как синоним термина «docstring» я использовал слова: «документация» и «документационные строки». Что же, перейдём к самому переводу.

PEP 257
Название: Соглашение о Docstrings
Авторы: David Goodger <goodger at python.org>, Guido van Rossum <guido at python.org>
Обсуждение: doc-sig at python.org
Статус: Активный
Тип: Информационный
Создано: 29-Мая-2001
Публикация: 13-Июнь-2001

Аннотация


Данный PEP документирует семантику и соглашения, связанные с использованием Python docstrings.

Обоснование


Целью данного PEP является стандартизация высокоуровневой структуры документационных строк: описать, что именно они должны содержать и объяснять (При этом мы не будем обговаривать фактический синтаксис разметки). PEP содержит не жесткие предписания, а рекомендации:
«Общепринятые соглашения обеспечивают ясность, логичность, удобство сопровождения и воспитывают хорошие программистские привычки. Но они не заставляет вас действовать против своей воли. Это Python!»

Тим Питерс на comp.lang.python, 2001-06-16

Если вы избегаете общепринятых соглашений, в худшем случае на вас посмотрят искоса. Но существуют некоторые приложения (Например, системы документирования, подобные Docutils), которые позволят вам добиться более качественного результата, если вы знаете о договорённостях и будете им следовать.

Спецификация


Что такое Docstring?


Документационная строка — это строковый литерал, являющийся первой инструкцией в определении модуля, функции, класса или метода. Такая строка становится доступна при обращении с специальному атрибуту __doc__ этого объекта.

Все библиотеки, а также функции и классы, экспортируемые ими, должны иметь docstring. Публичные методы (включая конструктор __ init__) также должны иметь документацию. Сам же пакет может быть задокументирован внутри файла __init__.py, находящегося в соответствующей ему директории.

Строковые литералы, встречающиеся в других местах кода, также могут играть роль документации. Они не распознаются Python компилятором байт-кода и недоступны в качестве атрибутов объекта во время выполнения программы (т. е. у них отсутствует информация в __ doc__ ). Но существуют два дополнительных типа документационных строк, которые извлекаются с помощью других программных средств:

  1. Строковые литералы, встречающиеся сразу после простого присваивания на уровне модуля, класса или метода __init__, называются «строки документации атрибута». (attribute docstrings)
  2. Строковые литералы, встречающиеся сразу после другой строки документации, называются «дополнительными строками документации». (additional docstrings)

Пожалуйста, смотрите PEP 258, «Спецификация проекта Docutils», где более подробно описываются attribute и additional docstrings.

[прим. ред. Пояснение из PEP 258]
def f(x):
    """Это  docstring расположенный в __doc__ функции."""
    """
    Это "additional docstrings", он проигнорируется компилятором, 
    но будет распознан средствами Docutils.
    """
    return x**2

f.a = 1
"""Это "attribute docstrings" для атрибута функции: f.a"""

Для согласованности всегда используйте """тройные двойные кавычки""" вокруг документационной строки. Если вы используете символы обратной косой черты ("\"), то воспользуйтесь r"""сырая строка с двойными кавычками""" в ваших документациях. Для docstring, содержащих Unicode символы, используйте u"""Юникод строка в тройных двойных кавычках""". [прим. unicode строки потеряли смысл в python 3.x]

Существует две формы docstring: однострочные и многострочный.

Однострочные документационные строки


Однострочные строки используются для очевидных случаев и они должны действительно находится на одной строке. Например:

def kos_root():
    """Вернёт путь к папке root KOS"""
    global _kos_root
    if _kos_root: return _kos_root
    ...

Замечания:

  • Тройные кавычки используются даже если строка помещается на одной линии. Это облегчает последующее расширение документации.
  • Закрывающие кавычки находятся на той же строке, что и открывающие. Для однострочных docstring это выглядит лучше.
  • Ни до, ни после документации не пропускаются строки. Код пишется сразу же на следующей линии
  • Документационная строка — это «фраза», заканчивающаяся точкой. Она описывает эффект функции или метода в командном тоне («Сделай это», «Верни это»), не являясь простым описанием. Например, не пишите подобные документации: «Возвращает путь ...».

    Примечание переводчика
    Значение фразы «командный тон» в английском языке можно пояснить на примере словосочетаний «Return pathname» и «Returns pathname». PEP-257 просит придерживаться первого стиля, потому что он более строгий.
  • Однострочная документация НЕ должна быть простой «подписью», повторяющей параметры функции/метода (которые могут быть получены путем инспектирования). Не делайте так:

    def function(a, b):
        """function(a, b) -> list"""
    

    Этот вид документаций подходит только для функций, написанных на языке C (таких как встроенные), где самоанализ невозможен. Также стоит упомянуть о природе возвращаемого значения. Предпочтительной формой для такой документационной строки будет что-то вроде:

    def function(a, b):
        def function(a, b):
        """Сделай X и верни список."""
    

    (И конечно-же, «Сделай X» дожно быть заменено полезным описанием!)

    Примечание переводчика
    Да, я только что писал выше: «Документация не является простым описанием», но в оригинале Гвидо и Дэвид действительно используют одинаковое слово «description» в этих двух местах. Думаю, что не стоит слишком придираться к этому, ведь посыл и так ясен.


Многострочные документационные строки


Многострочные документации состоят из сводной строки (summary line) имеющей такую же структуру, как и однострочный docstring, после которой следует пустая линия, а затем более сложное описание. «Summary line» может быть использована средствами автоматического документирования; поэтому так важно располагать её на одной строке и после делать пропуск в одну линию. Сводная строка пишется сразу после открывающихся кавычек, но допускается сделать перенос и начать со следующей строки. [прим. после этого предложения я был счастлив, ведь находились люди, которые упорно пытались мне доказать, что делать перенос ни в коем случае нельзя :-) ] При этом, весь docstring должен иметь такой же отступ, как и открывающие кавычки первой строки (см. пример ниже).

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

Документация скрипта (автономной программы) представляет из себя сообщение «о правильном использовании» и возможно будет напечатано, когда скрипт вызовется с неверными или отсутствующими аргументами (или же с опцией "-h", для получения «help»). Такая документационная строка должна описывать функционал и синтаксис параметров скрипта, а также переменные среды и используемые файлы. Данное сообщение может оказаться довольно сложным (мануал длиной в несколько полных экранов), но при этом оно должно быть удобным для новых пользователей, чтобы они смогли использовать команду правильно. Также мануал должен давать чёткое описание всех параметров и аргументов для более опытных пользователей.

Документация модуля должна обычно содержать список классов, исключений и функций (и любых других важных объектов), которые экспортируются при помощи библиотеки, а также однострочное пояснение для каждого из них. (Это резюме, как правило, даёт меньше деталей, чем summary line в docstring самого объекта). В документации пакета (т. е. docstring модуля в __init__.py ) следует также описать и перечислить модули и подпакеты, экспортируемые главным.

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

Документация класса должна обобщать его поведение и перечислять открытые методы, а также переменные экземпляра. Если класс будет иметь подклассы с дополнительный интерфейсом, то этот интерфейс должен быть указан отдельно (но всё также в этой документации). Конструктор класса должен иметь свою отдельную документационную строку для метода __init__. Независимые (индивидуальные) методы должны иметь собственную документацию.

Если класс является потомком и его поведение в основном наследуется от основного класса, в его документации необходимо упомянуть об этом и описать возможные различия. Используйте глагол «override» («переопределить»), чтобы указать факт подмены метода и что вследствие этого метод суперкласса вызываться не будет. Используйте глагол «extends» («расширяет»), если метод подкласса вызывает метод суперкласса (в дополнение к его собственному поведению).

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

def complex(real=0.0, imag=0.0):
    """Сформировать комплексное число.

    Ключевые аргументы:
    real -- действительная часть (по умолчанию 0.0)
    imag -- мнимая часть (по умолчанию 0.0)
    """
    if imag == 0.0 and real == 0.0:
        return complex_zero
    ...


Если весь docstring не помещается в строку, вы можете вынести закрывающие кавычки на отдельную линию. Таким образом, можно будет использовать Emacs команду: fill-paragraph

Обработка Docstring


Инструменты обработки документационных строк должны удалять одинаковое количество отступов, равное минимальному отступу всех не пустых строк, начиная со второй. Любые отступы в первой строке документации несущественны и будут удалены. Относительный отступ более поздних строк в строке документа сохраняется. Пустые строки должны быть удалены из начала и конца строки документа.

Поскольку код гораздо точнее слов, здесь приведена реализация алгоритма:

def trim(docstring):
    if not docstring:
        return ''
    # Конвертирует табы в пробелы (согласно нормам Python)
    # и разбивает в список из строк:
    lines = docstring.expandtabs().splitlines()
    # Высчитывает наименьший отступ (первая линия не считается):
    indent = sys.maxsize
    for line in lines[1:]:
        stripped = line.lstrip()
        if stripped:
            indent = min(indent, len(line) - len(stripped))
    # Убираем отступ (особенность первой линии документации):
    trimmed = [lines[0].strip()]
    if indent < sys.maxsize:
        for line in lines[1:]:
            trimmed.append(line[indent:].rstrip())
    # Убираем пустые строки из начала и конца:
    while trimmed and not trimmed[-1]:
        trimmed.pop()
    while trimmed and not trimmed[0]:
        trimmed.pop(0)
    # Возвращаем итоговую строку документации:
    return '\n'.join(trimmed)

Примечание переводчика
По сравнению с оригинальным кодом, в python3 атрибут sys.maxint был переименован в sys.maxsize, поэтому в коде это сразу исправлено.


Документация в следующем примере содержит два символа новой строки и поэтому имеет длину равную трём. Первая и последняя строки пусты:

def foo ():
    """
    Это вторая строка документации.
    """

Проиллюстрируем:

>>> print repr(foo.__doc__)
'\n     Это вторая строка документации.\n    '
>>> foo.__doc__.splitlines()
['', '     Это вторая строка документации.', '    ']
>>> trim(foo.__doc__)
' Это вторая строка документации.'

Таким образом, после обработки следующие документационные строки будут эквивалентны:

def foo():
    """Документация на
    несколько линий.
    """

def bar():
    """
    Документация на
    несколько линий.
    """

Примечание переводчика
Также не стоит забывать и про модуль inspect, где лежит уже готовая реализация функции форматирования документаций, поэтому с таким же успехом можно было написать: inspect.cleandoc(function.__doc__)

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Стоит ли продолжить переводить наиболее важные и интересные статьи PEP? Например, по одному разделу в неделю

  • 60,9%Да, стоит переводить, ведь это поможет новичкам70
  • 8,7%Нет, новички могут сами смогут разобраться с PEP, если захотят10
  • 30,4%Лучше делать статьи, основанные на PEP, где просто указаны главные моменты и привести побольше примеров35

Похожие публикации

AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 7

    +2
    Спасибо за статью; если бы не увидел это здесь, еще долго бы наверное откладывал чтение PEP'ов, тем более про докстринги. Продолжай в том же духе! Маленькое замечание – не хватило выделений в самом тексте, чтобы можно было быстро прыгнуть глазами к той или иной теме.
      +3

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


      Меня вот сейчас минусанули с пометкой: "уже знал эту информацию", поэтому вместо семантических соглашений, я решил заняться новыми PEP-ами: 570 и 572. (В пайтоне 3.8 они описывают новые чисто позиционные аргументы и присваивание через оператор ":=" ). Думаю это будет достаточно актуально.


      И чтобы было удобнее, буду делать две подчасти в статье: одна чистый перевод, а во второй кратко объясню ключевые моменты своими словами с примерами (что-то вроде напоминалки).


      А вот что делать с минусом "больше рекламы, чем информации" я не знаю :-). Просто скажу, что если есть какие-то пожелания к переводам/какие именно PEP-ы стоит перевести в первую очередь, буду рад выслушать

        +2
        ""«Вернёт путь к папке root KOS»""

        Так повелительное наклонение или нет? :)
          +1

          Будем считать, что правильные документации должны быть на английском) В оригинале так:
          "Return the pathname of the KOS root directory."

            +1

            Мне кажется, что это такая особенность именно английского языка, где повелительное наклонение формируется постановкой глагола в начале предложения. То есть действие идет первым словом. Для русского языка же стоит использовать настоящее время. Например "Возвращает", "устанавливает", "чинит"…

          0
          Спасибо за перевод, будем пользовать пренепременно!

          > Для docstring, содержащих Unicode символы, используйте u""«Юникод строка в тройных двойных кавычках»"".
          — потеряло актуальность в Python3.* и с прекращением поддержки Python2.7, о чём я бы добавил комментарий =)
          А то дальше идут кириллические примеры:
          """Сделай X и верни список."""
            0
            Спасибо, добавил примечание.

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

          Самое читаемое