Docstring — строковый литерал, размещённый первым выражением в модуле, пакете, классе, методе или функции Python, служащий для встроенной документации и доступный во время выполнения через атрибут
.__doc__и инструменты вродеhelp().Комментарий — текст, игнорируемый интерпретатором, начинающийся с
#(или многострочный в некоторых языках), предназначенный для пояснений к коду, задач, оговорок и примечаний разработчиков.
Ключевые различия и последствия выбора 📚🛠️
| Критерий | Docstring | Комментарий | 
|---|---|---|
| Назначение | Документирование публичного API: что делает модуль/класс/функция, параметры, возврат, исключения | Объяснить «почему» и «как именно» реализовано; отметить допущения, ограничения, хак/обход | 
| Доступ во время выполнения | Доступен через .__doc__,help(), IDE тултипы | Недоступен, виден только в исходниках | 
| Инструменты | Используется Sphinx/pdoc/pydoc, doctest, статическая документация | Линтеры ( flake8,ruff) для качества/обязательности; не попадает в документацию | 
| Место размещения | Первый литерал в объекте (модуль, класс, функция) | В любом месте строки/блока кода, обычно над строкой кода | 
| Формат | Обычно reStructuredText, Google, NumPy-стили; первая строка — краткое резюме | Свободный текст; теги TODO,FIXME, ссылки на задачи | 
| Потребители | Пользователи библиотеки, генераторы документации, интерактивные среды | Команда разработки, ревьюеры, вы сами через полгода 🙂 | 
| Влияние на API | Часть контракта: подсказки IDE, автогенерация SDK, пользовательские справки | Не влияет на внешний контракт, чисто внутреннее пояснение | 
| Размер/детализация | Полезно детально: описание параметров, примеры, ссылки | Коротко и по делу; избыточность вредит | 
| Производительность/вес | Хранится в объекте (может быть удалён сборщиком документации) | Не попадает в байткод как объект; игнорируется интерпретатором | 
Когда использовать docstring ✅
- Публичные функции, методы и классы — пользователи должны понять назначение и контракт.
- Модули и пакеты — описать роль, экспортируемые объекты, side effects, формат конфигурации.
- Функции с нетривиальными параметрами/возвратом — перечислить типы, валидные диапазоны, единицы измерения.
- Ожидаемые исключения — явно указать, что и когда выбрасывается.
- Примеры использования — короткие doctest-блоки и ссылки на руководства.
- Декораторы/контекстные менеджеры — уточнить контракт и гарантии.
Когда использовать комментарий 📝
- Объяснение «почему»: ссылки на тикеты, исследование производительности, стандарты, регрессы.
- Хрупкие или оптимизированные участки — описать инварианты, пред- и постусловия, алгоритмические тонкости.
- Локальные намерения: «разделяем по пробелам, потому что API X требует …».
- Маркировка задач: # TODO(user):,# FIXME,# HACKс контекстом и сроком.
- Подавление предупреждений линтера/тайпчекера: # noqa,# type: ignore[code]— пояснить причину.
Практика на 2025 год: короткие правила
- Правило витрины: всё, что видит пользователь модуля — документируй docstring; остальное — по ситуации.
- Первая строка docstring — одно предложение в повелительном наклонении; следующая пустая строка; далее подробности.
- Согласуй стиль: reST (для Sphinx) или Google/NumPy с napoleon-расширением.
- Комментарии пишем как протокол решения: причина и следствие, а не повторение кода.
- Не дублировать: если поведение уже ясно из сигнатуры и типов — комментарий не нужен.
Мини-примеры кода
def pct_change(current: float, previous: float) -> float:
    """Return percent change between current and previous values.
    Args:
        current: New value.
        previous: Baseline value (must be non-zero).
    Returns:
        Percent change in range (-inf, +inf).
    Raises:
        ZeroDivisionError: If previous == 0.
    Examples:
        >>> pct_change(120, 100)
        0.2
    """
    if previous == 0:
        raise ZeroDivisionError("previous must be non-zero")
    return (current - previous) / previous
# Используем двустороннюю очередь ради амортизированной O(1) на pops.
# См. сравнительный бенч: PERF-142.
from collections import deque
Чего избегать
- Не повторяйте код комментариями: # i = i + 1 увеличивает iбесполезно.
- Не кладите длинные руководства в docstring — лучше ссылка на документацию проекта.
- Не описывайте типы только словами в docstring — используйте аннотации типов.
- Не используйте docstring для приватных мелких хелперов, если нет особой причины.
Структура docstring (рекомендовано PEP 257)
- Краткое резюме одной строкой.
- Пустая строка.
- Подробности: параметры, возврат, исключения, примеры, ссылки.
Интеграция с инструментами и CI 🔧
Sphinx/pdoc собирают docstring в HTML/PDF, а расширение napoleon понимает Google/NumPy-форматы. doctest позволяет выполнять примеры из docstring в CI. Линтеры (ruff/flake8) проверяют наличие docstring на публичных объектах и качество комментариев. IDE (PyCharm, VS Code, JetBrains AI) показывают docstring в подсказках и автодополнении. Комментарии остаются локальными подсказками для разработчиков и ревью.
Краткий чек-лист
- API видно пользователю? — пиши docstring.
- Решение нетривиально или хрупко? — поясняющий комментарий рядом.
- Инварианты/исключения/пример использования? — в docstring.
- Временный костыль? — комментарий с причинами и ссылкой на задачу.
FAQ по смежным темам
Нужно ли документировать приватные функции?
 Если они критичны или сложны — краткий docstring допустим. Иначе достаточно хорошего названия и комментария «почему» в местах вызова.
Где документировать исключения: в docstring или комментарии?
 В docstring — те, что являются частью контракта (ожидаемые пользователем). В комментариях — внутренние детали обработки ошибок.
Какой стиль docstring выбрать: reST, Google или NumPy?
 Любой единый для проекта. Если используете Sphinx без плагинов — reST. С napoleon удобно Google/NumPy.
Могут ли тайпчекеры использовать docstring?
 Официально — нет, источником истины являются аннотации. Некоторые плагины умеют извлекать подсказки, но это нестабильно; лучше аннотировать код.
Где держать большие примеры и руководства?
 В отдельной документации/README. В docstring — короткий пример и ссылка на раздел руководства.
Нужен ли комментарий к подавлению предупреждений (например, # type: ignore)?
 Да, укажите причину и срок пересмотра: # type: ignore[call-arg] — несовместимая сигнатура до релиза v2, тикет #1234.
