https://habr.com/ru/post/499358/- Python
- Программирование
- Профессиональная литература
- Изучение языков
Привет, Хабр. Бывают моменты, когда тебе хочется максимально погрузиться в язык и понять все его тонкости. В случае Python один из лучших способов это сделать — читать на официальном сайте документацию и PEP-ы. В своё время я этого не делал, поскольку не мог понять многих «технических» моментов, а вариантов русского перевода не было. Сейчас же я решил сам перевести PEP-257, где рассказывается о правильном документировании кода, ведь наверняка это поможет новичкам лучше понять истинный «пайтоновский» подход к написанию кода. Я переводил примеры кода на русский язык, но только для того, чтобы лучше донести смысл. В реальном программировании старайтесь писать документационные строки на английском. Так же говорю сразу, что как синоним термина «docstring» я использовал слова: «документация» и «документационные строки». Что же, перейдём к самому переводу.
Аннотация
Данный PEP документирует семантику и соглашения, связанные с использованием Python docstrings.
Обоснование
Целью данного PEP является стандартизация высокоуровневой структуры документационных строк: описать, что именно они должны содержать и объяснять (При этом мы не будем обговаривать фактический синтаксис [прим. имеется ввиду, что ответственность за формализм переходит к Epytext, reST и т.д.]). PEP содержит соглашения, а не законы или конкретный синтаксис:
«Универсальное соглашение обеспечивает ясность, логичность, удобство сопровождения и основу для хороших навыков программирования. Но оно не заставляет вас действовать против своей воли. Это Python!»
Тим Питерс на comp.lang.python, 2001-06-16
Если вы избегаете общепринятых соглашений, в худшем случае на вас посмотрят немного невзрачно. Но существуют некоторых приложения (Например, системы документирования, подобные Docutils), которые позволят вам добиться более качественного результата, если вы будете осведомлены о принятых конвенциях и следовать им.
Спецификация
Что такое Docstring?
Документационная строка — это строковый литерал, который встречается как первая инструкция в определении модуля, функции, класса или метода. Такая строка становится доступна при обращении с специальному атрибуту __doc__ этого объекта.
Все библиотеки, а также функции и классы, экспортируемые ими, должны иметь docstring. Публичные методы (включая конструктор __ init__) также должны иметь документацию. Сам же пакет может быть задокументирован внутри файл __init__.py, находящегося в соотвутствующей ему директории.
Строковые литералы, встречающиеся в других местах кода, также могут играть роль документации. Они не распознаются Python компилятором байт-кода и недоступны в качестве атрибутов объекта во время выполнения программы (т. е. у них отсутствует информация в __ doc__ ). Но существуют два дополнительных типа документационных строк, которые извлекаются с помощью других программных средств:
- Строковые литералы, встречающиеся сразу после простого присваивания на уровне модуля, класса или метода __init__, называются «строки документации атрибута». (attribute docstrings)
- Строковые литералы, встречающиеся сразу после другой строки документации, называются «дополнительными строками документации». (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"""Юникод строка в тройных двойных кавычках""".
Существует две формы 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__)