Аннотации типов и документация кода · МДК.01.01 · разработка программных модулей
Глава 4.7
Докстринг:
где пишется и как читается
В этой главе разобрано устройство докстринга: в каком месте файла он должен стоять, чтобы Python его сохранил, как достать его из программы через __doc__, help() и модуль inspect, какие правила задаёт PEP 257 и чем отличаются однострочная и многострочная запись. В конце — восемь ошибок, из-за которых описание не доходит до читателя.
- Категория
- Синтаксис и стандарт
- Уровень
- Начальный
- Опирается на
- Глава 4.6, модули и пакеты
- Зачем это знать
- Писать докстринги так, чтобы их видели инструменты
§1Определение и место в файле
Докстринг — строковый литерал, стоящий первой инструкцией в модуле, классе, функции или методе. Python сохраняет такую строку в атрибуте __doc__ соответствующего объекта. Пишут докстринг в тройных кавычках: они позволяют занять несколько строк и оставляют внутри одинарные и двойные кавычки без экранирования.
Докстрингстрока документации
Стоит сразу после строки def, class или в самом начале файла. Ничего исполняемого перед ним быть не должно: как только выше появляется присваивание, импорт или другая инструкция, строка перестаёт быть докстрингом и становится обычным значением, которое вычисляется и отбрасывается.
Место в файле решает, сохранится строка или пропадёт. Ниже одна и та же функция с одной и той же строкой, сдвинутой на строку ниже.
Одна строка, два местапереключите вкладку
def normalize_name(name: str) -> str: """Приводит имя официанта к виду «Фамилия Имя».""" cleaned = " ".join(name.split()) return cleaned.title()
>>> normalize_name.__doc__
'Приводит имя официанта к виду «Фамилия Имя».'
ДокстрингСтрока стоит первой инструкцией тела, поэтому Python сохранил её в атрибуте __doc__. Её увидят help(), подсказка редактора и сборщик документации.
def normalize_name(name: str) -> str: cleaned = " ".join(name.split()) """Приводит имя официанта к виду «Фамилия Имя».""" return cleaned.title()
>>> print(normalize_name.__doc__)
None
Обычная строкаВыше неё стоит присваивание, поэтому строка перестала быть докстрингом. Python вычислил её как выражение и отбросил результат. Программа работает по-прежнему, ошибки нет, а описания нет нигде: подсказка редактора пустая.
Разница видна только из программы: во втором случае normalize_name.__doc__ равен None. По внешним признакам такое место отличается от первого пустой подсказкой редактора, и ошибки при запуске не возникает.
§2Где Python хранит докстринг
Докстринг доступен через атрибут __doc__ у любого объекта, который его имеет: модуля, класса, функции, метода. Атрибут — обычная строка, с ней можно работать как с любой другой.
>>> from app.utils.text import normalize_name >>> normalize_name.__doc__ 'Приводит имя официанта к виду «Фамилия Имя».' >>> import app.utils.text >>> app.utils.text.__doc__ 'Работа с именами и строками отчёта.' >>> print(len.__doc__) Return the number of items in a container.
Последняя строка показывает, что через тот же атрибут читаются описания встроенных функций: докстринги устроены одинаково и в вашем коде, и в стандартной библиотеке.
У многострочных докстрингов в __doc__ сохраняются и отступы, которыми текст был выровнен в файле. Чтобы получить текст без общего отступа, применяют inspect.getdoc:
>>> import inspect >>> from app.services.calculator import calculate_average >>> print(inspect.getdoc(calculate_average)) Считает среднюю оценку по списку. Args: grades: Оценки от 2 до 5, список не должен быть пустым. Returns: Среднее значение, округлённое до двух знаков. Raises: ValueError: Если список оценок пуст. Среднее по пустому списку не определено, поэтому нуль в этом случае не возвращается.
Ключ python -OO запускает программу без докстрингов: они удаляются, и __doc__ становится None. Такой запуск применяют для экономии памяти в готовых сборках. Программа, которая читает __doc__ своих функций и строит по нему поведение, с этим ключом работает неверно. Докстринг предназначен людям и инструментам сборки документации.
§3help и подсказка редактора
Встроенная функция help() печатает справку об объекте: подпись, докстринг и, для модулей и классов, перечень их содержимого. Она работает только на том, что записано в коде, и никаких описаний не добавляет.
>>> help(calculate_average)
Help on function calculate_average in module app.services.calculator:
calculate_average(grades: list[int]) -> float
Считает среднюю оценку по списку.
Args:
grades: Оценки от 2 до 5, список не должен быть пустым.
Returns:
Среднее значение, округлённое до двух знаков.
Raises:
ValueError: Если список оценок пуст. Среднее по пустому списку
не определено, поэтому нуль в этом случае не возвращается.
На модуле help() печатает его докстринг и перечень функций с первыми строками их описаний. Так проверяют, что описания на месте: у функции без докстринга в выводе остаётся одна подпись.
help() на модуле app.services.orders из эталонного архива. Раздел NAME — первая строка докстринга модуля, DESCRIPTION — остальной его текст, FUNCTIONS — функции модуля с подписями и описаниями. Python собрал эту справку из кода: своего текста он не добавляет. Двоеточие внизу — постраничный просмотр, выход по клавише q.VS Code показывает то же самое при наведении указателя на имя функции в месте её вызова: всплывает подпись с аннотациями и текст докстринга. Подсказка вызывается и с клавиатуры — CtrlCmd + K, затем CtrlCmd + I. Пустая подсказка означает, что докстринга нет или он стоит не на первом месте.
Разница между кодом с описаниями и без них видна в одной и той же подсказке. Сначала проект, в котором докстрингов ещё нет, следом тот же вызов после того, как описания написаны.
revenue_by_waiter в main.py, подсказка открыта. В ней только подпись: имя, параметры с типами и тип результата. Что означает top и в каких единицах суммы, придётся смотреть в файле функции.
§4Однострочный докстринг
Однострочная запись применяется, когда назначение выражается одним предложением, а параметры понятны из имён и аннотаций. Правила записи такие:
- Кавычки и текст на одной строке:
"""Текст.""" - Предложение заканчивается точкой.
- Пустых строк до и после нет.
- Описывается действие функции: «Возвращает…», «Считает…», «Записывает…».
def format_kopeks(kopeks: int) -> str: """Переводит сумму в копейках в строку вида «1 250,00 ₽».""" return f"{kopeks / 100:,.2f} ₽".replace(",", " ").replace(".", ",") def format_title(text: str, width: int = 40) -> str: """Центрирует заголовок отчёта по заданной ширине.""" return text.center(width, "=")
§5Многострочный докстринг
Многострочная запись нужна, когда у функции есть параметры с ограничениями, возвращаемое значение требует пояснения или функция возбуждает исключения. Устройство такое: краткая строка, пустая строка, подробности, закрывающие кавычки на отдельной строке.
def load_orders(path: Path) -> list[Order]: """Читает заказы из CSV-файла. # краткая строка: одно предложение Имена официантов в файле записаны по-разному, поэтому здесь они приводятся к одному виду: по ним потом группируются заказы. # пустая строка отделяет разделы Args: path: Путь к файлу с колонками waiter и kopeks. Returns: Список заказов в порядке следования строк файла. Raises: FileNotFoundError: Если файла по указанному пути нет. ValueError: Если в колонке kopeks не целое число. """ # кавычки на отдельной строке
Комментарии справа в примере добавлены для разбора и в коде не пишутся. Порядок частей одинаков во всех форматах из главы 4.8, различается только запись разделов.
| Часть | Что в ней | Обязательна |
|---|---|---|
| Краткая строка | Одно предложение о действии функции, начинается сразу после кавычек | Да |
| Пустая строка | Отделяет краткую строку от подробностей | Да, если есть подробности |
| Пояснение | Смысл, ограничения, побочные действия, связь с другими частями проекта | По необходимости |
| Параметры | Перечень аргументов и что в них допустимо | Если ограничения не видны из подписи |
| Результат | Что означает возвращаемое значение | Если функция возвращает значение |
| Исключения | Какие ошибки возбуждаются и при каких данных | Если функция возбуждает исключения |
§6Правила PEP 257
PEP 257 — документ стандарта Python, описывающий оформление докстрингов. Он задаёт общие правила записи: в каких кавычках, где переносы, о чём первая строка. Разметку разделов он не определяет; форматы из главы 4.8 надстраиваются над этими правилами.
| Правило | Как это выглядит |
|---|---|
| Тройные двойные кавычки, даже для одной строки | """Текст.""" |
| Первая строка — краткое описание действия, заканчивается точкой | """Считает среднюю оценку.""" |
| Текст начинается сразу после кавычек, без переноса | """Считает… вместо """ |
| В многострочном — пустая строка после краткого описания | см. §5 |
| Закрывающие кавычки многострочного — на своей строке | """ |
| Докстринг есть у модуля, публичной функции, класса и метода | правило курса из главы 4.6 |
| Описание сообщает, что функция делает | """Возвращает список заказов.""" |
Про первую строку PEP 257 требует описывать действие функции, и в англоязычных проектах это записывают повелительной формой: Return the list of orders. В русском тексте тот же смысл передаётся формой третьего лица: «Возвращает список заказов». В курсе принята эта форма, и она должна быть одинаковой во всём проекте.
"""Загрузить заказы.""""""Функция для чтения файла""""""отчёт"""
"""Читает заказы из файла.""""""Возвращает текст отчёта.""""""Записывает отчёт в файл."""
§7Докстринг модуля и пакета
Докстринг модуля — первая строка файла, до импортов. Он называет, за что файл отвечает, и попадает в раздел DESCRIPTION при вызове help() на модуле.
"""Заказы: чтение из CSV и расчёты по ним. Модуль работает только с данными заказов и ничего не печатает. Форматирование отчёта — в app/utils/formatter.py. """ import csv from pathlib import Path
У пакета докстринг пишется в файле __init__.py: этот файл представляет пакет целиком. В нём указывают назначение пакета и перечень того, что он предоставляет наружу.
"""Служебный слой: расчёты и чтение данных.
Пакет содержит модули calculator и orders. Функции этих модулей
вызываются из main.py и не зависят от способа вывода.
"""
Перечислять внутри докстринга все функции модуля не требуется: их список help() и сборщик документации получают из самого кода.
§8Докстринг класса и метода
Докстринг класса стоит первой строкой после class и описывает, что представляет объект этого класса. Атрибуты перечисляют в отдельном разделе. У методов свои докстринги, по тем же правилам, что у функций.
class Shift: """Смена кафе: заказы одного рабочего дня. Объект накапливает заказы и считает по ним итоги. Заказы хранятся в порядке добавления. Attributes: day: Дата смены. orders: Добавленные заказы в порядке поступления. """ def __init__(self, day: date) -> None: """Создаёт пустую смену на указанную дату.""" self.day = day self.orders: list[Order] = [] def add(self, order: Order) -> None: """Добавляет заказ в конец списка смены. Args: order: Заказ с уже нормализованным именем официанта. """ self.orders.append(order) def total_kopeks(self) -> int: """Возвращает выручку смены в копейках; у пустой смены — 0.""" return sum(order.kopeks for order in self.orders)
Описание того, что объект собой представляет, идёт в докстринг класса, а перечень полей — в раздел Attributes:. Описание создания и проверок при создании относится к __init__. Здесь проверок нет и параметр один, поэтому у __init__ хватает одной строки.
Как этот докстринг выглядит в справке, показано в главе 4.9, случай 10: help(Shift) печатает описание класса, раздел Attributes и следом перечень методов с их описаниями.
§9Восемь ошибок
| Ошибка | Что получается | Как правильно |
|---|---|---|
| Строка стоит после первой инструкции | __doc__ равен None, подсказка пустая | Поставить строку сразу после def |
Одинарные кавычки: 'Текст' | Работает, но нарушает стандарт, а кавычка внутри текста обрывает строку | Тройные двойные кавычки |
| Пересказ кода построчно | Описание длиннее функции и устаревает после первой правки | Назначение, ограничения, результат |
| Перечисление типов словами | Дублирует аннотации, расходится с ними после правки | Типы в подписи, смысл в докстринге |
| Описание осталось от прежнего поведения | Читатель получает неверные сведения и не может их проверить | Править докстринг в том же коммите, что и функцию |
| Незадокументированные исключения | В вызывающем коде не видно, какие ошибки перехватывать | Раздел с перечнем ошибок |
| Докстринг вместо комментария внутри тела | Пояснение к строке кода попадает в справку и на сайт документации | Пояснения к реализации — комментарием |
Пустая строка между def и докстрингом | Строка остаётся докстрингом, но стандарт нарушен, проверка выдаёт замечание | Без пустой строки |
§10Мини-практика: пять минут
Шаги выполняются на стартовом архиве проекта темы из главы 4.6.
- Распакуйте архив, откройте папку в VS Code и найдите файл
app/services/calculator.py. Добавьте докстринг модуля первой строкой файла, до импортов. - Добавьте однострочный докстринг функции
calculate_average. - Запустите
python -i -c "import app.services.calculator as c"и выполнитеhelp(c). В выводе должны быть докстринг модуля и список функций. - Выполните
print(c.calculate_average.__doc__)и сверьте текст с файлом. - Перенесите докстринг функции на строку ниже, после первой инструкции тела, и повторите предыдущую команду. Вывод —
None. - Верните докстринг на место. Наведите указатель на вызов функции в
main.py: в подсказке появился текст описания.
§11Проверьте себя
Почему строка документации должна быть первой инструкцией в функции?
Python сохраняет в __doc__ только строковый литерал, стоящий первым в теле. Если выше есть любая инструкция, строка вычисляется как обычное выражение и нигде не сохраняется: __doc__ остаётся None, подсказка редактора пуста, в собранную документацию описание не попадает.
Чем inspect.getdoc отличается от __doc__?
__doc__ возвращает строку как она записана в файле, вместе с отступами выравнивания. inspect.getdoc убирает общий отступ, и текст печатается без лишних пробелов слева.
Что напечатает help() для функции без докстринга?
Имя функции, её подпись с аннотациями и имя модуля. Описания в выводе не будет: брать его негде. По этому признаку недокументированные функции находят вручную, а программой это проверяют правилами из главы 4.10.
Где пишется докстринг пакета?
В файле __init__.py этого пакета, первой строкой файла. Этот файл представляет пакет целиком, поэтому его докстринг показывается при вызове help() на имени пакета.
Что описывают в докстринге класса, а что — в докстринге __init__?
В докстринге класса — что представляет объект и какие у него атрибуты. В докстринге __init__ — параметры создания, проверки входных значений и исключения при недопустимых данных. Если проверок нет и параметры понятны из аннотаций, докстринг __init__ не пишут.
Функция изменилась, а докстринг остался прежним. Чем это хуже отсутствия докстринга?
Без описания читателю приходится открыть код. Неверное описание читатель принимает за верное и строит на нём вызывающий код, а расхождение обнаруживается при работе программы. Поэтому докстринг правят в том же изменении, что и поведение функции.
§12Шпаргалка по главе
первая инструкция объекта
после def / class
в начале файла — у модуля
"""три двойные"""
закрывающие — своей строкой
f.__doc__
help(f)
inspect.getdoc(f)
наведение на имя
Cmd/Ctrl+K, затем Cmd/Ctrl+I
краткая строка + точка
пустая строка
параметры, результат, ошибки
Считает… Возвращает… Записывает…
одинаково во всём проекте
Итог главы
Докстринг — строковый литерал, стоящий первой инструкцией в модуле, классе, функции или методе. Python сохраняет его в атрибуте __doc__, откуда его читают help(), модуль inspect, подсказка редактора и сборщик документации. Строка, стоящая после первой инструкции тела, докстрингом не становится, и ошибки при этом не возникает: описание не доходит до читателя. Однострочная запись годится, когда назначение выражается одним предложением. Многострочная состоит из краткой строки, пустой строки и разделов о параметрах, результате и исключениях; закрывающие кавычки стоят на отдельной строке. Эти правила задаёт PEP 257, а разметку разделов — форматы, которые разобраны в следующей главе: одна и та же функция записана в них четырьмя способами.
- Правила оформления и требование к первой строке — PEP 257, Docstring Conventions.
inspect.getdocи удаление общего отступа — документация модуля inspect.- Удаление докстрингов ключом
-OO— описание параметров командной строки Python.
Комментарий документации:
где пишется и как читается
В этой главе разобрано устройство комментария документации Go: в каком месте файла он должен стоять, чтобы его увидели инструменты, как прочитать его командой go doc и в подсказке редактора, какие правила задаёт руководство Go Doc Comments и чем отличаются короткая и подробная запись. В конце — восемь ошибок, из-за которых описание не доходит до читателя.
- Категория
- Синтаксис и правила
- Уровень
- Начальный
- Опирается на
- Глава 4.6, пакеты Go
- Зачем это знать
- Писать комментарии так, чтобы их видели инструменты
§1Определение и место в файле
Комментарий документации — комментарий, который стоит непосредственно перед объявлением верхнего уровня: package, func, type, var или const. Инструменты Go — go doc, gopls, pkgsite — считают его описанием этого объявления. Записывают его строками, начинающимися с //; запись /* … */ тоже допустима, но в коде на Go почти не встречается.
Комментарий документацииdoc comment
Стоит вплотную над объявлением, без пустой строки между последней строкой комментария и словом func, type и другими. Объявление должно быть верхнего уровня, то есть вне функций: комментарий над переменной внутри тела функции документацией не становится.
Место в файле решает, попадёт ли текст в документацию. Ниже одна и та же функция из internal/utils/text.go с одним и тем же комментарием: в первой вкладке он стоит вплотную, во второй его отделяет пустая строка.
Один комментарий, два местапереключите вкладку
// NormalizeName приводит имя официанта к виду «Фамилия Имя». func NormalizeName(name string) string {
package utils // import "example.com/otchet-kafe/internal/utils"
func NormalizeName(name string) string
NormalizeName приводит имя официанта к виду «Фамилия Имя».
ДокументацияКомментарий стоит вплотную над объявлением, поэтому go doc напечатал его под подписью. Его же покажут подсказка редактора и pkgsite.
// NormalizeName приводит имя официанта к виду «Фамилия Имя». func NormalizeName(name string) string {
package utils // import "example.com/otchet-kafe/internal/utils" func NormalizeName(name string) string
Обычный комментарийПустая строка оторвала комментарий от объявления. Программа собирается и работает по-прежнему, ошибки нет, а описания нет нигде: go doc печатает одну подпись, подсказка редактора пустая. Найти такое место помогает revive: он сообщит, что у NormalizeName нет комментария.
Разница видна только инструментам: текст в файле на месте, и человек, открывший файл, его прочитает. Тот, кто вызывает функцию из другого пакета и смотрит подсказку, описания не увидит.
§2Команда go doc
Команда go doc печатает документацию в терминале. Ей передают путь к пакету и, если нужно, имя внутри него. Путь к пакету своего модуля начинается с ./: ./internal/utils — пакет в этой папке. Пакеты стандартной библиотеки называют по имени: strings, fmt. Все команды ниже выполнены в папке третьего архива.
| Команда | Что печатает |
|---|---|
go doc ./internal/utils | комментарий пакета и подписи всех экспортируемых имён, без их описаний |
go doc ./internal/utils NormalizeName | подпись и полный комментарий одной функции |
go doc ./internal/domain Shift | объявление типа с полями, его комментарий и перечень методов |
go doc ./internal/domain Shift.Add | один метод типа |
go doc -all ./internal/ui | пакет целиком: все имена с полными описаниями |
go doc -src ./internal/utils TodayLabel | исходный текст объявления вместе с комментарием |
go doc strings.Fields | то же для стандартной библиотеки |
На пакете go doc печатает его комментарий и перечень того, что пакет предоставляет. Так проверяют, что описание пакета на месте:
$ go doc ./internal/services
package services // import "example.com/otchet-kafe/internal/services"
Package services — служебный слой: чтение данных и расчёты.
Функции пакета работают только с данными и ничего не печатают.
var ErrNoGrades = errors.New("список оценок пуст")
var ErrNoOrders = errors.New("список заказов пуст")
func CalculateAverage(grades []int) (float64, error)
func GradeBounds(grades []int) (low, high int, err error)
func LoadOrders(path string) ([]domain.Order, error)
func ReadRows(path string) iter.Seq2[map[string]string, error]
func SaveReport(path string, lines []string) (int, error)
func SortOrdersInPlace(orders []domain.Order)
type WaiterRevenue struct{ ... }
func RevenueByWaiter(orders []domain.Order, top int) ([]WaiterRevenue, error)
Первая строка — имя пакета и путь, по которому его импортируют. Ниже — комментарий пакета, затем переменные, функции и типы. RevenueByWaiter напечатана с отступом под типом WaiterRevenue: go doc относит к типу функции, которые возвращают значения этого типа, — так их проще найти. Описаний функций в этом перечне нет: чтобы прочитать описание, передают имя.
С флагом -all печатается всё сразу. Так выглядит небольшой пакет ui:
$ go doc -all ./internal/ui
package ui // import "example.com/otchet-kafe/internal/ui"
Package ui выводит готовый отчёт пользователю.
FUNCTIONS
func PrintReport(lines []string)
PrintReport печатает готовый отчёт в консоль построчно.
Функция только печатает: расчёты и форматирование выполняются до вызова,
в пакетах services и utils.
Текст комментария go doc заново разбивает на строки по ширине около 80 знаков, поэтому переносы в выводе не совпадают с переносами в файле. Абзацы при этом сохраняются: их разделяет строка из одного //. Через ту же команду читаются описания стандартной библиотеки: go doc strings.Fields печатает комментарий из исходников Go, записанный по тем же правилам, что и комментарии проекта.
go doc читает исходники с диска: файлы своего модуля, кэш скачанных модулей и исходники стандартной библиотеки, которые ставятся вместе с Go. Интернет для него не нужен. pkgsite из главы 4.6 показывает тот же текст в браузере.
§3Подсказка редактора
VS Code показывает комментарий документации при наведении указателя на имя в месте вызова: под подписью идёт текст комментария. Подсказка вызывается и с клавиатуры — CtrlCmd + K, затем CtrlCmd + I. Пустая подсказка означает, что комментария нет или он стоит не вплотную.
Разница видна в одной и той же подсказке. Сначала второй архив, в котором комментариев ещё нет, следом третий, задокументированный. В обоих курсор стоит на вызове RevenueByWaiter в main.go.
main.go. В подсказке только подпись: имя с пакетом, параметры с типами и типы результатов. Что означает top, в каком порядке идут официанты и когда возвращается ошибка, придётся смотреть в файле функции. Нижняя строка services.RevenueByWaiter on pkg.go.dev — ссылка на общий сайт документации. Проект курса там не опубликован, поэтому по ней ничего не откроется; документацию своего проекта смотрят в pkgsite из главы 4.6.
top и когда возвращается ErrNoOrders. Имя ErrNoOrders выделено цветом: в комментарии оно записано в квадратных скобках, и подсказка показывает его ссылкой. Текст взят из комментария над func RevenueByWaiter целиком.§4Короткий комментарий
Короткая запись применяется, когда назначение выражается одним предложением, а параметры понятны из имён и типов. Правила записи такие:
- первое слово — имя того, что описывается:
// TodayLabel возвращает…; - дальше — полное предложение о действии: «возвращает…», «считает…», «записывает…»;
- предложение заканчивается точкой;
- между комментарием и объявлением нет пустой строки.
// TodayLabel возвращает текущую дату в виде «21.09.2026» для шапки отчёта. func TodayLabel() string { // NewShift создаёт пустую смену на указанную дату. func NewShift(day time.Time) *Shift { // Add добавляет заказ в конец списка смены. func (s *Shift) Add(order Order) {
Почему комментарий начинается с имени: строка комментария тогда понятна и без объявления рядом. Поиск по файлам проекта выдаёт отдельные строки, и строка // TodayLabel возвращает текущую дату… сама сообщает, о какой функции речь. Первое слово-имя проверяет revive — правило разобрано в §9.
§5Подробный комментарий
Подробная запись нужна, когда у параметров есть ограничения, результат требует пояснения или функция возвращает ошибку. Устройство такое: первое предложение о действии, пустая строка комментария //, затем абзацы с подробностями.
// LoadOrders читает заказы из файла смены с колонками waiter и kopeks. ← первое предложение // ← граница абзаца // Заказы возвращаются в порядке строк файла. Ошибка возвращается, если // файла нет или в колонке kopeks записано не целое число. func LoadOrders(path string) ([]domain.Order, error) {
Пометки со стрелками справа добавлены для разбора и в коде не пишутся. В отличие от Python, в Go нет разделов вроде Args: и Returns:. Параметры, результат и ошибки описывают обычными предложениями, называя параметры по имени: «параметр top ограничивает…», «для пустого списка возвращается [ErrNoOrders]». Так написаны и комментарии стандартной библиотеки — например, у strings.Fields.
| Часть | Что в ней | Обязательна |
|---|---|---|
| Первое предложение | Имя и действие: что делает функция | Да |
Строка // | Отделяет абзацы | Да, если есть подробности |
| Пояснение | Смысл, ограничения, порядок результата, побочные действия | По необходимости |
| Параметры | Что допустимо в параметре — словами, с именем параметра | Если ограничения не видны из подписи |
| Ошибки | В каком случае возвращается ошибка, лучше с именем переменной ошибки | Если в результатах есть error |
§6Правила Go Doc Comments
Go Doc Comments — руководство на сайте go.dev, которое описывает, как пишутся комментарии документации и как инструменты их показывают. В Go оно одно на весь язык: отдельных форматов, как у Python, нет. Основные правила:
| Правило | Как это выглядит |
|---|---|
| Комментарий вплотную над объявлением | без пустой строки перед func |
| Первое слово — имя описываемого | // LoadOrders читает… |
| Полные предложения с точкой в конце | // Add добавляет заказ в конец списка смены. |
Комментарий пакета начинается словами Package имя | // Package utils форматирует строки… |
Абзацы разделяет строка // | см. §5 |
| Описание сообщает, что функция делает и что возвращает, а не как она устроена | подробности реализации — обычным комментарием в теле |
Функция, которая возвращает bool, описывается словами «сообщает, …ли» | // IsBig сообщает, больше ли сумма порога. |
Про форму первого предложения. В англоязычном коде Go пишут // LoadOrders reads…: имя, затем глагол в третьем лице. В русском тексте та же форма: «LoadOrders читает…», «Add добавляет…». В курсе она принята для всего проекта. Если имени нет или оно не первое, revive выдаёт замечание.
// Загрузить заказы.// функция для чтения файла// отчёт
// LoadOrders читает заказы из файла.// SaveReport записывает отчёт в файл.// PrintReport печатает отчёт.
§7Комментарий пакета и программы
Комментарий пакета стоит над строкой package и начинается словами Package имя. Он называет, за что отвечает пакет, и печатается первым в выводе go doc на пакете. У пакета обычно несколько файлов, а комментарий пакета пишут в одном из них. Если написать его в двух файлах, инструменты склеят оба текста в один.
// Package services — служебный слой: чтение данных и расчёты. // // Функции пакета работают только с данными и ничего не печатают. package services
В пакете services четыре файла, и комментарий пакета есть только в calculator.go; в importer.go, orders.go и report_file.go строка package services стоит без комментария. В больших пакетах для комментария заводят отдельный файл doc.go, в котором кроме комментария и строки package ничего нет: так его проще найти.
У пакета main комментарий описывает программу: что она делает и как её запустить. Слов Package main в нём не пишут — читателю программы они ничего не сообщают.
// Программа «Отчёт кафе» читает файл смены, считает выручку // по официантам и печатает отчёт в консоль. // // Запуск из папки с go.mod: // // go run . package main
Строка // go run . начинается с табуляции после //. Так в комментарии записывают блок кода или команду: инструменты покажут его моноширинным шрифтом и не станут переносить по словам. Этот и другие элементы разметки разобраны в главе 4.8. Перечислять в комментарии пакета все его функции не нужно: их перечень go doc и pkgsite получают из самого кода.
§8Тип, поля и методы
Комментарий типа стоит над словом type и описывает, что представляет значение этого типа. Поля структуры описывают коротким комментарием в конце строки поля — отдельного раздела, как Attributes: в Python, нет. У методов свои комментарии, по тем же правилам, что у функций.
// Order — один заказ из файла смены. // // Создавать заказ нужно через [NewOrder]: она проверяет сумму // и приводит имя официанта к виду «Фамилия Имя». type Order struct { Waiter string // имя официанта в виде «Фамилия Имя» Kopeks int // сумма заказа в копейках, неотрицательная } // NewOrder создаёт заказ с проверкой данных. // // Имя официанта нормализуется функцией [utils.NormalizeName]. // Если сумма отрицательная, возвращается [ErrNegativeKopeks]. func NewOrder(waiter string, kopeks int) (Order, error) {
В комментарии типа сказано, что заказ представляет и как его правильно создавать. Единицы суммы — копейки — записаны у поля Kopeks: из типа int их не видно. Проверки при создании описаны у конструктора NewOrder: в нём они и выполняются.
Так этот текст печатает go doc: объявление типа вместе с комментариями полей, затем комментарий типа и функции, которые его создают.
$ go doc ./internal/domain Order
package domain // import "example.com/otchet-kafe/internal/domain"
type Order struct {
Waiter string // имя официанта в виде «Фамилия Имя»
Kopeks int // сумма заказа в копейках, неотрицательная
}
Order — один заказ из файла смены.
Создавать заказ нужно через NewOrder: она проверяет сумму и приводит имя
официанта к виду «Фамилия Имя».
func NewOrder(waiter string, kopeks int) (Order, error)
§9Восемь ошибок
| Ошибка | Что получается | Как правильно |
|---|---|---|
| Пустая строка между комментарием и объявлением | go doc и подсказка показывают одну подпись; revive: should have comment or be unexported | Комментарий вплотную над func |
Первое слово — не имя: // Приводит имя… | Описание показывается, но revive: comment on exported function NormalizeName should be of the form "NormalizeName ..." | // NormalizeName приводит… |
| Пересказ кода построчно | Описание длиннее функции и устаревает после первой правки | Назначение, ограничения, результат |
| Перечисление типов словами | Дублирует подпись, расходится с ней после правки | Типы в подписи, смысл в комментарии |
| Описание осталось от прежнего поведения | Читатель получает неверные сведения и не может их проверить | Править комментарий в том же коммите, что и функцию |
| Не сказано, когда возвращается ошибка | Вызывающий код не знает, какие ошибки проверять через errors.Is | Предложение со случаем и именем ошибки: [ErrNoOrders] |
| Пояснение к реализации в комментарии документации | Подробности устройства попадают в подсказку и на сайт | Пояснения к строкам кода — комментарием в теле |
| Комментарий пакета в нескольких файлах | Тексты склеиваются в один, и описание пакета читается вперемешку | Один файл — например, doc.go |
Первые две ошибки получены на проекте темы: замечания revive в таблице — его настоящий вывод. Ни одна из восьми ошибок не мешает сборке: компилятор комментарии не читает. Их находят программой проверки из главы 4.10 и чтением на ревью.
§10Мини-практика: пять минут
Шаги выполняются на втором архиве проекта из главы 4.6.
- Откройте файл
internal/services/calculator.go. Добавьте над строкойpackage servicesкомментарий пакета, начиная со слов// Package services. - Добавьте короткий комментарий над
func CalculateAverage, начиная с имени функции. - Выполните в терминале
go doc ./internal/services. В выводе должен появиться комментарий пакета. - Выполните
go doc ./internal/services CalculateAverageи сверьте текст с файлом. - Вставьте пустую строку между комментарием и
funcи повторите предыдущую команду. В выводе останется одна подпись. - Уберите пустую строку. Откройте
internal/services/calculator_test.goи наведите указатель на вызовCalculateAverageв первом тесте: в подсказке появился текст описания.
§11Проверьте себя
Почему комментарий должен стоять вплотную над объявлением?
Инструменты Go считают описанием объявления только комментарий, который заканчивается на строке прямо над ним. Пустая строка отрывает текст: он остаётся в файле, но go doc, подсказка редактора и pkgsite его не показывают.
Чем отличаются go doc ./internal/utils и go doc -all ./internal/utils?
Первая команда печатает комментарий пакета и подписи экспортируемых имён без их описаний. Вторая печатает пакет целиком: каждое имя с полным комментарием.
Что напечатает go doc для функции без комментария?
Строку пакета и подпись функции. Описания не будет: брать его негде. Такие функции программой находит revive, это разобрано в главе 4.10.
Где пишется комментарий пакета?
Над строкой package в одном из файлов пакета, со слов Package имя. В большом пакете для него заводят файл doc.go. У пакета main комментарий описывает программу и слов Package main не содержит.
Как описать поля структуры?
Коротким комментарием в конце строки поля: Kopeks int // сумма заказа в копейках. go doc печатает эти комментарии вместе с объявлением типа.
Функция изменилась, а комментарий остался прежним. Чем это хуже отсутствия комментария?
Без описания читателю приходится открыть код. Неверное описание читатель принимает за верное и строит на нём вызывающий код, а расхождение обнаруживается при работе программы. Поэтому комментарий правят в том же изменении, что и поведение функции.
§12Шпаргалка по главе
вплотную над func, type, var
над package — у пакета
// Имя действие.
// — граница абзаца
go doc ./pkg Имя
go doc -all ./pkg
go doc -src ./pkg Имя
наведение на имя
Cmd/Ctrl+K, затем Cmd/Ctrl+I
первое предложение с именем
пустая строка //
ограничения, результат, ошибки
// Package utils …
Kopeks int // в копейках
Итог главы
Комментарий документации — комментарий вплотную над объявлением верхнего уровня. Его читают go doc, подсказка редактора и pkgsite; комментарий, отделённый пустой строкой, становится обычным, и ошибки при этом не возникает: описание просто не доходит до читателя. Первое предложение начинается с имени описываемого и сообщает действие. Подробности идут абзацами через строку //; разделов для параметров и результата в Go нет, их описывают предложениями. Комментарий пакета начинается словами Package имя, поля структуры описывают в конце строки. В следующей главе разобрана разметка внутри комментария — ссылки, списки, блоки кода — и примеры, которые проверяются запуском.
- Место комментария, форма первого предложения, комментарии пакета и программы — Go Doc Comments.
- Флаги
go doc— документация команды go. - Правило
exportedпрограммы revive — описание правил revive. - Выводы
go docиreviveполучены на архивах проекта темы с Go1.27и revive1.17; кадры подсказки сняты в VS Code1.138с расширением Go0.56и gopls0.24.