Тема 04 · Аннотации типов и документация кода

Аннотации типов и документация кода · МДК.01.01 · разработка программных модулей

Глава 4.7

4.7

Докстринг:
где пишется и как читается

В этой главе разобрано устройство докстринга: в каком месте файла он должен стоять, чтобы Python его сохранил, как достать его из программы через __doc__, help() и модуль inspect, какие правила задаёт PEP 257 и чем отличаются однострочная и многострочная запись. В конце — восемь ошибок, из-за которых описание не доходит до читателя.

Категория
Синтаксис и стандарт
Уровень
Начальный
Опирается на
Глава 4.6, модули и пакеты
Зачем это знать
Писать докстринги так, чтобы их видели инструменты

§1Определение и место в файле

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

Докстрингстрока документации

Стоит сразу после строки def, class или в самом начале файла. Ничего исполняемого перед ним быть не должно: как только выше появляется присваивание, импорт или другая инструкция, строка перестаёт быть докстрингом и становится обычным значением, которое вычисляется и отбрасывается.

Место в файле решает, сохранится строка или пропадёт. Ниже одна и та же функция с одной и той же строкой, сдвинутой на строку ниже.

Одна строка, два местапереключите вкладку

app/utils/text.pyстрока стоит сразу после def
def normalize_name(name: str) -> str:
    """Приводит имя официанта к виду «Фамилия Имя»."""
    cleaned = " ".join(name.split())
    return cleaned.title()
что вернёт программа
Терминалpython -i
>>> normalize_name.__doc__
'Приводит имя официанта к виду «Фамилия Имя».'

ДокстрингСтрока стоит первой инструкцией тела, поэтому Python сохранил её в атрибуте __doc__. Её увидят help(), подсказка редактора и сборщик документации.

app/utils/text.pyта же строка на одну строку ниже
def normalize_name(name: str) -> str:
    cleaned = " ".join(name.split())
    """Приводит имя официанта к виду «Фамилия Имя»."""
    return cleaned.title()
что вернёт программа
Терминалpython -i
>>> print(normalize_name.__doc__)
None

Обычная строкаВыше неё стоит присваивание, поэтому строка перестала быть докстрингом. Python вычислил её как выражение и отбросил результат. Программа работает по-прежнему, ошибки нет, а описания нет нигде: подсказка редактора пустая.

Разница видна только из программы: во втором случае normalize_name.__doc__ равен None. По внешним признакам такое место отличается от первого пустой подсказкой редактора, и ошибки при запуске не возникает.

§2Где Python хранит докстринг

Докстринг доступен через атрибут __doc__ у любого объекта, который его имеет: модуля, класса, функции, метода. Атрибут — обычная строка, с ней можно работать как с любой другой.

Терминалpython -i
>>> 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:

Терминалpython -i
>>> 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() печатает справку об объекте: подпись, докстринг и, для модулей и классов, перечень их содержимого. Она работает только на том, что записано в коде, и никаких описаний не добавляет.

Терминалpython -i
>>> 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() печатает его докстринг и перечень функций с первыми строками их описаний. Так проверяют, что описания на месте: у функции без докстринга в выводе остаётся одна подпись.

Терминал VS Code: вывод help для модуля app.services.orders с разделами NAME, DESCRIPTION и FUNCTIONS
Вывод help() на модуле app.services.orders из эталонного архива. Раздел NAME — первая строка докстринга модуля, DESCRIPTION — остальной его текст, FUNCTIONS — функции модуля с подписями и описаниями. Python собрал эту справку из кода: своего текста он не добавляет. Двоеточие внизу — постраничный просмотр, выход по клавише q.

VS Code показывает то же самое при наведении указателя на имя функции в месте её вызова: всплывает подпись с аннотациями и текст докстринга. Подсказка вызывается и с клавиатуры — CtrlCmd + K, затем CtrlCmd + I. Пустая подсказка означает, что докстринга нет или он стоит не на первом месте.

Разница между кодом с описаниями и без них видна в одной и той же подсказке. Сначала проект, в котором докстрингов ещё нет, следом тот же вызов после того, как описания написаны.

Подсказка VS Code на вызове revenue_by_waiter в проекте без докстрингов: показана только подпись функции
Проект без описаний. Курсор стоит на вызове revenue_by_waiter в main.py, подсказка открыта. В ней только подпись: имя, параметры с типами и тип результата. Что означает top и в каких единицах суммы, придётся смотреть в файле функции.
Подсказка VS Code на том же вызове в задокументированном проекте: подпись, описание и разделы Args, Returns, Raises
Тот же вызов в задокументированном проекте. Под подписью появилась краткая строка докстринга, ниже разделы Args и Returns: редактор разметил их заголовками и отступами. Подсказка прокручивается, дальше в ней раздел Raises. Текст взят из докстринга функции целиком.

§4Однострочный докстринг

Однострочная запись применяется, когда назначение выражается одним предложением, а параметры понятны из имён и аннотаций. Правила записи такие:

  • Кавычки и текст на одной строке: """Текст."""
  • Предложение заканчивается точкой.
  • Пустых строк до и после нет.
  • Описывается действие функции: «Возвращает…», «Считает…», «Записывает…».
app/utils/formatter.pyоднострочные докстринги
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Многострочный докстринг

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

app/services/orders.pyстроение многострочного докстринга
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() на модуле.

app/services/orders.pyпервые строки файла
"""Заказы: чтение из CSV и расчёты по ним.

Модуль работает только с данными заказов и ничего не печатает.
Форматирование отчёта — в app/utils/formatter.py.
"""

import csv
from pathlib import Path

У пакета докстринг пишется в файле __init__.py: этот файл представляет пакет целиком. В нём указывают назначение пакета и перечень того, что он предоставляет наружу.

app/services/__init__.pyдокстринг пакета
"""Служебный слой: расчёты и чтение данных.

Пакет содержит модули calculator и orders. Функции этих модулей
вызываются из main.py и не зависят от способа вывода.
"""

Перечислять внутри докстринга все функции модуля не требуется: их список help() и сборщик документации получают из самого кода.

§8Докстринг класса и метода

Докстринг класса стоит первой строкой после class и описывает, что представляет объект этого класса. Атрибуты перечисляют в отдельном разделе. У методов свои докстринги, по тем же правилам, что у функций.

app/domain/shift.pyкласс, атрибуты, методы
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.

  1. Распакуйте архив, откройте папку в VS Code и найдите файл app/services/calculator.py. Добавьте докстринг модуля первой строкой файла, до импортов.
  2. Добавьте однострочный докстринг функции calculate_average.
  3. Запустите python -i -c "import app.services.calculator as c" и выполните help(c). В выводе должны быть докстринг модуля и список функций.
  4. Выполните print(c.calculate_average.__doc__) и сверьте текст с файлом.
  5. Перенесите докстринг функции на строку ниже, после первой инструкции тела, и повторите предыдущую команду. Вывод — None.
  6. Верните докстринг на место. Наведите указатель на вызов функции в 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)
Подсказка в VS Code наведение на имя Cmd/Ctrl+K, затем Cmd/Ctrl+I
Строение краткая строка + точка пустая строка параметры, результат, ошибки
Форма первой строки Считает… Возвращает… Записывает… одинаково во всём проекте

Итог главы

Докстринг — строковый литерал, стоящий первой инструкцией в модуле, классе, функции или методе. Python сохраняет его в атрибуте __doc__, откуда его читают help(), модуль inspect, подсказка редактора и сборщик документации. Строка, стоящая после первой инструкции тела, докстрингом не становится, и ошибки при этом не возникает: описание не доходит до читателя. Однострочная запись годится, когда назначение выражается одним предложением. Многострочная состоит из краткой строки, пустой строки и разделов о параметрах, результате и исключениях; закрывающие кавычки стоят на отдельной строке. Эти правила задаёт PEP 257, а разметку разделов — форматы, которые разобраны в следующей главе: одна и та же функция записана в них четырьмя способами.

Откуда взяты факты
  1. Правила оформления и требование к первой строке — PEP 257, Docstring Conventions.
  2. inspect.getdoc и удаление общего отступа — документация модуля inspect.
  3. Удаление докстрингов ключом -OO — описание параметров командной строки Python.
4.7

Комментарий документации:
где пишется и как читается

В этой главе разобрано устройство комментария документации 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 с одним и тем же комментарием: в первой вкладке он стоит вплотную, во второй его отделяет пустая строка.

Один комментарий, два местапереключите вкладку

internal/utils/text.goкомментарий сразу над func
// NormalizeName приводит имя официанта к виду «Фамилия Имя».
func NormalizeName(name string) string {
Терминалgo doc ./internal/utils NormalizeName
package utils // import "example.com/otchet-kafe/internal/utils"

func NormalizeName(name string) string
    NormalizeName приводит имя официанта к виду «Фамилия Имя».

ДокументацияКомментарий стоит вплотную над объявлением, поэтому go doc напечатал его под подписью. Его же покажут подсказка редактора и pkgsite.

internal/utils/text.goмежду комментарием и func пустая строка
// NormalizeName приводит имя официанта к виду «Фамилия Имя».

func NormalizeName(name string) string {
Терминалgo doc ./internal/utils NormalizeName
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
$ 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
$ 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 doc читает исходники с диска: файлы своего модуля, кэш скачанных модулей и исходники стандартной библиотеки, которые ставятся вместе с Go. Интернет для него не нужен. pkgsite из главы 4.6 показывает тот же текст в браузере.

§3Подсказка редактора

VS Code показывает комментарий документации при наведении указателя на имя в месте вызова: под подписью идёт текст комментария. Подсказка вызывается и с клавиатуры — CtrlCmd + K, затем CtrlCmd + I. Пустая подсказка означает, что комментария нет или он стоит не вплотную.

Разница видна в одной и той же подсказке. Сначала второй архив, в котором комментариев ещё нет, следом третий, задокументированный. В обоих курсор стоит на вызове RevenueByWaiter в main.go.

Подсказка VS Code на вызове RevenueByWaiter в проекте без комментариев: только подпись функции
Второй архив, строка 17 файла main.go. В подсказке только подпись: имя с пакетом, параметры с типами и типы результатов. Что означает top, в каком порядке идут официанты и когда возвращается ошибка, придётся смотреть в файле функции. Нижняя строка services.RevenueByWaiter on pkg.go.dev — ссылка на общий сайт документации. Проект курса там не опубликован, поэтому по ней ничего не откроется; документацию своего проекта смотрят в pkgsite из главы 4.6.
Подсказка VS Code на том же вызове в задокументированном проекте: под подписью текст комментария документации
Третий архив. В нём над функциями есть комментарии, поэтому вызов сместился на строку 28. Под подписью появился текст комментария документации двумя абзацами: что считает функция и в каком порядке результат, затем что значит top и когда возвращается ErrNoOrders. Имя ErrNoOrders выделено цветом: в комментарии оно записано в квадратных скобках, и подсказка показывает его ссылкой. Текст взят из комментария над func RevenueByWaiter целиком.

§4Короткий комментарий

Короткая запись применяется, когда назначение выражается одним предложением, а параметры понятны из имён и типов. Правила записи такие:

  • первое слово — имя того, что описывается: // TodayLabel возвращает…;
  • дальше — полное предложение о действии: «возвращает…», «считает…», «записывает…»;
  • предложение заканчивается точкой;
  • между комментарием и объявлением нет пустой строки.
internal/utils/clock.go и internal/domain/shift.goкороткие комментарии
// 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Подробный комментарий

Подробная запись нужна, когда у параметров есть ограничения, результат требует пояснения или функция возвращает ошибку. Устройство такое: первое предложение о действии, пустая строка комментария //, затем абзацы с подробностями.

internal/services/orders.goстроение подробного комментария
// 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 на пакете. У пакета обычно несколько файлов, а комментарий пакета пишут в одном из них. Если написать его в двух файлах, инструменты склеят оба текста в один.

internal/services/calculator.goпервые строки файла
// Package services — служебный слой: чтение данных и расчёты.
//
// Функции пакета работают только с данными и ничего не печатают.
package services

В пакете services четыре файла, и комментарий пакета есть только в calculator.go; в importer.go, orders.go и report_file.go строка package services стоит без комментария. В больших пакетах для комментария заводят отдельный файл doc.go, в котором кроме комментария и строки package ничего нет: так его проще найти.

У пакета main комментарий описывает программу: что она делает и как её запустить. Слов Package main в нём не пишут — читателю программы они ничего не сообщают.

main.goкомментарий программы
// Программа «Отчёт кафе» читает файл смены, считает выручку
// по официантам и печатает отчёт в консоль.
//
// Запуск из папки с go.mod:
//
//	go run .
package main

Строка // go run . начинается с табуляции после //. Так в комментарии записывают блок кода или команду: инструменты покажут его моноширинным шрифтом и не станут переносить по словам. Этот и другие элементы разметки разобраны в главе 4.8. Перечислять в комментарии пакета все его функции не нужно: их перечень go doc и pkgsite получают из самого кода.

§8Тип, поля и методы

Комментарий типа стоит над словом type и описывает, что представляет значение этого типа. Поля структуры описывают коротким комментарием в конце строки поля — отдельного раздела, как Attributes: в Python, нет. У методов свои комментарии, по тем же правилам, что у функций.

internal/domain/order.goтип, поля, конструктор
// 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
$ 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.

  1. Откройте файл internal/services/calculator.go. Добавьте над строкой package services комментарий пакета, начиная со слов // Package services.
  2. Добавьте короткий комментарий над func CalculateAverage, начиная с имени функции.
  3. Выполните в терминале go doc ./internal/services. В выводе должен появиться комментарий пакета.
  4. Выполните go doc ./internal/services CalculateAverage и сверьте текст с файлом.
  5. Вставьте пустую строку между комментарием и func и повторите предыдущую команду. В выводе останется одна подпись.
  6. Уберите пустую строку. Откройте 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 Имя
Подсказка в VS Code наведение на имя Cmd/Ctrl+K, затем Cmd/Ctrl+I
Строение первое предложение с именем пустая строка // ограничения, результат, ошибки
Пакет и поля // Package utils … Kopeks int // в копейках

Итог главы

Комментарий документации — комментарий вплотную над объявлением верхнего уровня. Его читают go doc, подсказка редактора и pkgsite; комментарий, отделённый пустой строкой, становится обычным, и ошибки при этом не возникает: описание просто не доходит до читателя. Первое предложение начинается с имени описываемого и сообщает действие. Подробности идут абзацами через строку //; разделов для параметров и результата в Go нет, их описывают предложениями. Комментарий пакета начинается словами Package имя, поля структуры описывают в конце строки. В следующей главе разобрана разметка внутри комментария — ссылки, списки, блоки кода — и примеры, которые проверяются запуском.

Откуда взяты факты
  1. Место комментария, форма первого предложения, комментарии пакета и программы — Go Doc Comments.
  2. Флаги go doc — документация команды go.
  3. Правило exported программы revive — описание правил revive.
  4. Выводы go doc и revive получены на архивах проекта темы с Go 1.27 и revive 1.17; кадры подсказки сняты в VS Code 1.138 с расширением Go 0.56 и gopls 0.24.
Глава 4.7 · Докстринг: где пишется и как читается · МДК.01.01 Автор: Макаров Максим Николаевич