$ sudo teach IT
МОДУЛЬ 10 · УРОК 1

Зачем документировать и что такое docstring

Вы когда-нибудь открывали свой старый код через полгода и думали: «Кто это написал? Что это вообще делает?» Спойлер: это написали вы. В этом уроке разберёмся, как писать код, который будет понятен — и вам, и коллегам — через месяц, год и навсегда.

⏱ ~30 минут 🎓 Для новичков 🐍 Python 3

📖 Главный принцип: код читают чаще, чем пишут

Когда начинаешь программировать, кажется, что написать код — это самое главное. Нажал Run — работает, задача выполнена. Но в реальной разработке всё немного иначе.

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

«Программы должны писаться для людей,
и лишь изредка — для компьютеров.»

— Харольд Абельсон, один из авторов классической книги SICP

Компьютеру всё равно, как вы назвали переменную и есть ли у функции описание. Он выполняет инструкции — и точка. Зато человеку, который будет читать ваш код через три месяца, совсем не всё равно.

⚠️ Важно понять прямо сейчас: документирование кода — это не бюрократия и не формальность. Это профессиональная забота о людях, которые будут читать ваш код. В первую очередь — о себе самом в будущем.

🧠 «Будущий ты через полгода — это другой человек»

Это звучит немного философски, но это правда. Давайте разберёмся почему.

📅 Типичная ситуация

Сентябрь: вы пишете функцию для расчёта скидки. Вы только что придумали алгоритм, у вас в голове всё ясно — зачем писать объяснение к очевидным вещам?

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

Посмотрите на простой пример. Угадайте, что делает эта функция:

def calc(x, y, z):
    return x * (1 - y / 100) if z else x

Что такое x? Что такое y? Что такое z? Когда возвращается x, а когда x * (1 - y / 100)? Чтобы понять — нужно читать весь код вокруг, искать где функция вызывается...

А теперь посмотрите на ту же функцию, но с документацией:

def apply_discount(price, discount_percent, is_sale):
    """
    Применяет скидку к цене товара.

    Если товар участвует в акции (is_sale=True),
    возвращает цену с учётом скидки.
    Иначе возвращает исходную цену без изменений.
    """
    return price * (1 - discount_percent / 100) if is_sale else price

Мгновенно понятно! Хорошее имя функции + докстринг — и никаких вопросов. Код говорит сам за себя.

👥 Командная разработка: твой код будут читать коллеги

Если вы работаете в команде — документирование становится ещё важнее. Ваш коллега не знает, что у вас было в голове, когда вы писали функцию. У него нет вашего контекста.

🔍

Онбординг нового разработчика

Новый человек в команде разбирается с проектом. Хорошие docstring'и — это самая быстрая документация: он открывает функцию и сразу видит, что она делает.

🐛

Поиск бага

Коллега ищет ошибку в вашем коде. Без docstring он тратит часы на разбор алгоритма. С docstring — видит намерение и быстро находит расхождение между «что должно быть» и «что есть».

🔧

Code review

Ревьюер проверяет ваш код перед тем, как он попадёт в основную ветку. Docstring помогает ему понять задумку, а не только реализацию — это делает ревью быстрее и точнее.

📚

Автоматическая документация

Инструменты вроде Sphinx берут docstring'и прямо из кода и генерируют красивую HTML-документацию. Написал docstring — получил документацию бесплатно.

💼 Профессиональная культура: в серьёзных компаниях отсутствие docstring в публичных методах — это замечание на code review. Так же, как опечатка в резюме: технически не критично, но говорит о небрежности.

📝 Что такое docstring — строка-документация

Docstring (от англ. documentation string — строка документации) — это специальная строка, которую вы пишете сразу после объявления функции, метода, класса или модуля.

Ключевое слово здесь — «сразу после». Docstring — это не просто комментарий в произвольном месте. Это первая строка тела функции/класса/модуля. Именно это место делает её особенной — Python сам сохраняет её как часть объекта.

🗝️ Где можно писать docstring

  • В начале функции — после def имя_функции(...):
  • В начале метода класса — точно так же, после def
  • В начале класса — после class ИмяКласса:
  • В самом начале модуля (файла .py) — самая первая строка файла

✍️ Синтаксис: тройные кавычки """..."""

Docstring записывается в тройных кавычках — либо двойных """...""", либо одинарных '''...'''. В Python принято использовать двойные.

Тройные кавычки нужны потому, что docstring может быть многострочным — тройные кавычки позволяют написать текст на нескольких строках без дополнительных символов.

Однострочный docstring — когда описание помещается в одну строку:

def greet(name):
    """Возвращает приветственное сообщение для пользователя."""
    return f"Привет, {name}!"

Многострочный docstring — когда нужно описать подробнее:

def calculate_area(width, height):
    """
    Вычисляет площадь прямоугольника.

    Принимает ширину и высоту в сантиметрах.
    Возвращает площадь в квадратных сантиметрах.
    """
    return width * height

📐 Соглашение по форматированию (PEP 257)

  • Однострочный: открывающие и закрывающие """ на одной строке, точка в конце
  • Многострочный: первая строка — краткое описание, потом пустая строка, потом подробности, закрывающие """ на отдельной строке
  • Текст внутри """ пишут с большой буквы

🔬 Атрибут __doc__ — docstring живёт в объекте

Вот что делает docstring особенным — Python не просто «читает» эту строку и забывает. Он сохраняет её как атрибут объекта под именем __doc__. Это значит, что вы можете получить docstring в любой момент во время работы программы!

Двойные подчёркивания с обеих сторон (__doc__) — это в Python признак «магического» или «специального» атрибута. Такие атрибуты Python создаёт и использует сам. __doc__ — один из них.

def greet(name):
    """Возвращает приветственное сообщение для пользователя."""
    return f"Привет, {name}!"


# Получаем docstring через атрибут __doc__
print(greet.__doc__)
# Вывод: Возвращает приветственное сообщение для пользователя.


class Dog:
    """Класс, описывающий собаку с именем и породой."""

    def bark(self):
        """Заставляет собаку лаять."""
        return "Гав!"


# Docstring доступен и у класса, и у его методов
print(Dog.__doc__)
# Вывод: Класс, описывающий собаку с именем и породой.

print(Dog.bark.__doc__)
# Вывод: Заставляет собаку лаять.

🧩 Запомните: docstring — это не просто текст в коде. Это часть объекта. Функция greet — это объект, и у неё есть атрибут __doc__ со значением «Возвращает приветственное сообщение...». Точно так же, как у неё есть имя (greet.__name__).

📦 Docstring для класса и модуля

Docstring пишут не только для функций. Классы и целые файлы (модули) тоже документируют. Посмотрим на полный пример:

"""
Модуль для работы с банковскими счетами.

Содержит класс BankAccount с методами для пополнения,
снятия и проверки баланса.
"""


class BankAccount:
    """
    Банковский счёт с базовыми операциями.

    Позволяет хранить баланс и выполнять
    операции пополнения и снятия средств.
    """

    def __init__(self, owner, balance=0):
        """
        Создаёт новый банковский счёт.

        owner  — имя владельца счёта (строка)
        balance — начальный баланс (число, по умолчанию 0)
        """
        self.owner = owner
        self.balance = balance

    def deposit(self, amount):
        """Пополняет счёт на указанную сумму. Возвращает новый баланс."""
        self.balance += amount
        return self.balance

    def withdraw(self, amount):
        """
        Снимает деньги со счёта.

        Если средств недостаточно, вызывает ValueError.
        Возвращает новый баланс при успехе.
        """
        if amount > self.balance:
            raise ValueError("Недостаточно средств")
        self.balance -= amount
        return self.balance

Теперь доступ к документации:

import bank  # представим, что файл называется bank.py

print(bank.__doc__)
# Модуль для работы с банковскими счетами...

print(BankAccount.__doc__)
# Банковский счёт с базовыми операциями...

print(BankAccount.withdraw.__doc__)
# Снимает деньги со счёта...

🆘 Функция help() — удобный просмотр docstring

Python предоставляет встроенную функцию help(), которая показывает документацию объекта в красивом, читаемом виде. Именно из __doc__ атрибута она берёт информацию.

Вы можете вызвать help() прямо в интерпретаторе Python или в Jupyter Notebook — это очень удобно при изучении незнакомого кода.

def apply_discount(price, discount_percent, is_sale):
    """
    Применяет скидку к цене товара.

    Параметры:
        price           — исходная цена (число)
        discount_percent — размер скидки в процентах (число от 0 до 100)
        is_sale         — участвует ли товар в акции (True/False)

    Возвращает цену с учётом скидки, если is_sale=True.
    Иначе возвращает исходную цену без изменений.
    """
    return price * (1 - discount_percent / 100) if is_sale else price


# Вызываем help() в интерпретаторе:
help(apply_discount)

Вывод в интерпретаторе будет выглядеть вот так:

Help on function apply_discount in module __main__:

apply_discount(price, discount_percent, is_sale)
    Применяет скидку к цене товара.

    Параметры:
        price           — исходная цена (число)
        discount_percent — размер скидки в процентах (число от 0 до 100)
        is_sale         — участвует ли товар в акции (True/False)

    Возвращает цену с учётом скидки, если is_sale=True.
    Иначе возвращает исходную цену без изменений.

💡 Попробуйте прямо сейчас: в интерпретаторе Python напишите help(print) или help(len) — и увидите docstring встроенных функций. Именно так работает вся документация стандартной библиотеки Python.

⚖️ Docstring vs комментарий — в чём разница

В Python есть два способа оставить текстовое пояснение к коду: комментарий через # и docstring через """...""". Многие новички считают их одинаковыми — но между ними принципиальная разница.

# Комментарий

  • Начинается с символа #
  • Python полностью игнорирует его при выполнении
  • Не сохраняется нигде — «испаряется»
  • Недоступен через __doc__ или help()
  • Используется для пояснения как работает конкретная строка кода
  • Пишется в произвольном месте

"""Docstring"""

  • Тройные кавычки """
  • Python сохраняет как атрибут __doc__
  • Живёт внутри объекта всё время работы программы
  • Доступен через obj.__doc__ и help(obj)
  • Описывает что делает функция/класс/модуль
  • Только первая строка тела объявления

Смотрите на примере — как они живут вместе:

def calculate_tax(price, rate):
    """Вычисляет сумму налога для заданной цены и ставки."""
    # rate — это процент, поэтому делим на 100
    # например rate=20 означает НДС 20%
    return price * rate / 100


# Docstring сохранён и доступен:
print(calculate_tax.__doc__)
# Вывод: Вычисляет сумму налога для заданной цены и ставки.

# А комментарии — нет, они исчезли при выполнении:
# print(calculate_tax.comments)  <-- такого атрибута не существует

🎯 Простое правило для запоминания:

Комментарий (#) — объясняет как работает этот кусок кода. Для разработчиков, читающих исходник.
Docstring (""") — объясняет что делает функция/класс. Для пользователей API, IDE и инструментов документации.

✅ Где обязательно писать docstring

Docstring — это хороший тон, но не всегда обязательный. Вот чёткое правило: если что-то «публичное» (то есть предназначено для использования другими), — docstring обязателен.

✅

Публичные классы

Любой класс, который вы ожидаете использовать — сами или другие. Docstring объясняет, для чего класс существует.

✅

Публичные методы и функции

Все методы без подчёркивания в начале имени. Имена вида calculate(), save(), get_price() — публичные. Каждый должен иметь docstring.

✅

Модули (файлы .py)

Первые строки файла — коротко: что находится в этом модуле, какую задачу он решает. Особенно важно для библиотек и пакетов.

🔶

Приватные методы (опционально)

Методы с одним подчёркиванием _helper() — предназначены для внутреннего использования. Docstring желателен, но не обязателен. Если логика сложная — пишите.

📋 Практическое правило

Задайте себе вопрос: «Если кто-то вызовет эту функцию/создаст этот класс — ему нужно что-то знать кроме имени?» Если да — пишите docstring. Функция add(a, b) очевидна. Функция normalize_score(value, context) — требует объяснения.

✅ Итоги урока

  • Код читается чаще, чем пишется — пишите для людей: для коллег, для себя будущего, для тех, кто будет поддерживать ваш код.
  • «Будущий ты через полгода — другой человек»: без документации вы сами не поймёте свой код через несколько месяцев.
  • Docstring — это строка в тройных кавычках """...""", которая пишется сразу после объявления функции, класса или модуля.
  • Атрибут __doc__: Python сохраняет docstring как часть объекта — его можно прочитать в любой момент через obj.__doc__.
  • help(obj) показывает docstring в красивом виде — именно так работает документация стандартной библиотеки Python.
  • Docstring ≠ комментарий: комментарий (#) исчезает при выполнении, docstring живёт в объекте. Комментарий объясняет как, docstring объясняет что.
  • Писать docstring обязательно для всех публичных классов, методов и функций — это профессиональный стандарт.

👉 В следующем уроке мы познакомимся с форматом Google Style docstring — наиболее популярным соглашением в современном Python-коде. Вы научитесь описывать параметры, возвращаемые значения и исключения так, как это делают в Google, Яндексе и других крупных компаниях.