docstring в Python: документация внутри функции
Документация живёт внутри функции: строка в тройных кавычках после def, атрибут __doc__ и честный формат короткого описания.
Редакция Питоники
Функцию пишет один человек, а читают — другие, включая вас через полгода. Имя и аннотации из прошлого урока говорят, что принимает и возвращает функция, но не говорят, что она делает и в каких пределах думает. Для этого в Python есть docstring — строка документации, которая живёт прямо внутри функции. Это не комментарий мимо кода, а часть самой функции: её можно достать программно, и на неё опирается справка.
Первая документация: строка сразу после def
docstring — это строка в тройных кавычках сразу после def: она живёт в атрибуте __doc__ и попадает в справку help(). Тройные кавычки нужны на случай многострочного текста; короткое описание умещается и в одну строку.
def celsius_to_fahrenheit(celsius):
"""Переводит градусы Цельсия в Фаренгейты."""
return celsius * 1.8 + 32
print(celsius_to_fahrenheit(100))
print(celsius_to_fahrenheit.__doc__)
212.0 Переводит градусы Цельсия в Фаренгейты.
Атрибут __doc__ — обычная строка, приклеенная к функции: её можно напечатать, измерить длиной, передать дальше. Python положил туда docstring автоматически, ничего присваивать не пришлось. Обратите внимание: печать идёт напрямую из атрибута, без справочной системы.
Многострочный docstring и чистый текст
Длинное описание пишут в несколько строк: первая — короткое резюме, дальше — аргументы и результат. Внутри __doc__ строка хранится как написано, вместе с отступами, поэтому для аккуратной печати подключают inspect.getdoc из стандартной библиотеки — он возвращает чистый текст без лишних отступов:
import inspect
def normalize_spaces(text):
"""Сжимает повторы пробелов в один.
Аргументы:
text: строка, которую надо почистить.
Возвращает:
строку без лишних пробелов.
"""
return ' '.join(text.split())
print(normalize_spaces(' привет мир '))
print(inspect.getdoc(normalize_spaces))
привет мир
Сжимает повторы пробелов в один.
Аргументы:
text: строка, которую надо почистить.
Возвращает:
строку без лишних пробелов.Печатный текст совпал с написанным — тот же порядок, те же строки. Разделы с Аргументами и результатом — не жёсткий синтаксис, а хорошая привычка: любой человек прочёл описание и знает, что функция вернёт, не открывая тело.
help(len)
docstring против комментария
Комментарий со решёткой и docstring живут рядом, но служат разному. Комментарий объясняет код тому, кто его правит; docstring объясняет функцию тому, кто её вызывает. Инструменты видят только docstring — вот функции с одинаковым телом и разной документацией:
def slugify(title):
"""Превращает заголовок в слаг для ссылки."""
return title.lower().replace(' ', '-')
def process(title):
"""обрабатывает строку"""
return title.lower().replace(' ', '-')
print(slugify('Мой Первый Пост'))
print(process('Мой Первый Пост'))
мой-первый-пост мой-первый-пост
Машина выдала одинаковый результат — тела идентичны. Разница для человека: по документации slugify понятно, что вернётся и зачем; process обещает «обработку», то есть ничего не обещает. docstring пишут для вызова, а не для компилятора.
docstring строго привязан к месту: документацией становится только строка, стоящая первой в теле. Любое выражение раньше неё — и атрибут __doc__ останется пустым, точнее None:
def broken():
result = 1
"""Эта строка не docstring: она не первая в теле."""
return result
print(broken.__doc__)
None
Что писать в docstring
Формат короткого описания из трёх частей: первая строка — глагол и суть; раздел Аргументов — по строке на каждый; раздел Возврата — что получит вызывающий:
import inspect
def discount(price, percent):
"""Считает цену со скидкой.
Аргументы:
price: цена до скидки.
percent: скидка в процентах.
Возвращает:
цену после скидки.
"""
return price * (100 - percent) / 100
print(discount(500, 20))
print(inspect.getdoc(discount))
400.0
Считает цену со скидкой.
Аргументы:
price: цена до скидки.
percent: скидка в процентах.
Возвращает:
цену после скидки.Пример достаточно короткий, чтобы читать его глазами, и достаточно честный, чтобы не лезть в тело. Для функций-однострочников хватит и одной строки — как у celsius_to_fahrenheit: многострочность нужна, когда есть что объяснить про аргументы.
| Раздел | Что писать | Пример |
|---|---|---|
| Первая строка | Один глагол и суть | Считает цену со скидкой |
| Аргументы | По строке на аргумент: имя и смысл | price: цена до скидки |
| Возврат | Что получит вызывающий | цену после скидки |
| Типы | Если аннотаций нет — укажи словами | price: целое число |
Длину документации можно измерить прямо — __doc__ это строка, и len работает:
def area(width, height):
"""Площадь прямоугольника: ширина на высоту."""
return width * height
print(area(3, 4))
print(len(area.__doc__))
12 41
Почему у lambda нет документации
lambda из урока про lambda — функция без имени, и docstring в ней не написать: тело — одно выражение, первой строкой там не разразишься документацией. Это ещё один аргумент за def, когда функцию будут читать чужие:
def doubled(n):
"""Возвращает удвоенное число."""
return n * 2
double = lambda x: x * 2
print(doubled(3), double(3))
print(double.__doc__)
6 6 None
if __name__ == '__main__':
import doctest
doctest.testmod()
- первая строка — глагол: Переводит, Считает, Превращает; без слов «функция» и «данная» — их видно и так;
- аргументы — по строке на каждый: имя и смысл, а не тип, если он уже стоит в аннотации;
- возврат — одной строкой: что получит вызывающий, особенно если возможен None;
- коротко: docstring, который длиннее тела функции, — сигнал, что описание надо сжать.
Документация закончила образ функции: имя, аннотации и docstring — три строки визитки, по которым функцию можно вызвать, не читая тело. Остался последний стройный навык — ошибки: что происходит, когда вызов не совпал с такой визиткой. Следующий урок разбирает TypeError и его друзей.
docstring у класса и метода
Документация есть не только у функций. У класса docstring пишется сразу после строки class, у метода — сразу после def. Это первое, что читают, когда открывают чужой модуль: что это за объект и зачем он.
class Report:
"""Отчёт по продажам за месяц."""
def __init__(self, title):
"""Создаёт отчёт с заголовком."""
self.title = title
def line(self, value):
"""Возвращает строку отчёта для одного значения."""
return f'{self.title}: {value}'
print(Report.__doc__)
print(Report.line.__doc__)
Отчёт по продажам за месяц. Возвращает строку отчёта для одного значения.
У каждого объекта свой __doc__: у класса — про класс, у метода — про метод. Ничего общего с __init__ это не имеет — конструктор здесь просто обычная функция, которой досталась собственная документация. У встроенных типов документация есть тоже: print(str.__doc__) покажет, что умеет строка.
Как читают чужую документацию
Документация — текст, который пишут для других, и читают её тремя способами. Первый — навести курсор на имя функции в редакторе: всплывающая подсказка покажет первую строку docstring, если он есть. Второй — help(функция) в терминале: полная справка с сигнатурой и описанием, с возможностью листать. Третий — print(inspect.getdoc(функция)): чистый текст без пейджера, удобно, когда разбираешь чужой код в блокноте.
Третий способ мы уже использовали в блоках урока — и сделали это специально: печать через __doc__ и inspect.getdoc даёт обычные строки, которые можно сравнить, сохранить в файл или показать в отчёте. help() на странице не запускается: он открывает интерактивный пейджер и ждёт нажатий клавиш.
Что писать, а что не писать
| Не пишем в docstring | Почему |
|---|---|
| «Функция складывает числа» | Это видно из имени и из подписи — вызывающему нужно другое |
| Копию аннотаций типов | Типы уже стоят в заголовке, повторять их — шум |
| Историю проекта и планы на будущее | Это не про функцию, а про проект: такое живёт в README |
| Списки задач вида TODO | Обещание читателю даёт поведение, а не список дел |
| Подробный разбор алгоритма | Это объяснение кода — ему место в комментарии |
Рабочее правило простое: docstring отвечает на вопрос «как мне этим пользоваться», а не «как это устроено внутри». Всё, что видно из подписи, повторять не надо. И наоборот — если из имени и подписи неясно, что вернётся при пустых данных или при None, это стоит написать.
doctest: примеры в документации проверяются
В Python есть приём, который мы в команде полюбили: примеры внутри docstring можно прогнать как тесты. Строка, начинающаяся с >>>, — это вызов, а следующая строка — ожидаемый результат.
import doctest
def double(n):
"""Удваивает число.
>>> double(3)
6
"""
return n * 2
print(doctest.testmod())
TestResults(failed=0, attempted=1)
Результат читается прямо: failed=0, attempted=1 — один пример, ни одной ошибки. Если код разъедется с документацией, doctest покажет расхождение прямо в тексте. Мы держим правило: у публичной функции с нетривиальным поведением в docstring есть хотя бы один пример. Он стоит три строки и заменяет половину вопросов «а как это работает».
Практика: визитка функции целиком
Соберём вместе всё, что мы знаем о функции: имя, аннотации типов и документацию. Это и есть та самая визитка, по которой функцию вызывают, не открывая тело.
import inspect
def average_price(prices: list[int]) -> float:
"""Считает среднюю цену по списку.
Аргументы:
prices: список цен в рублях.
Возвращает:
среднее арифметическое по списку.
"""
return sum(prices) / len(prices)
print(average_price([100, 200, 300]))
print(inspect.getdoc(average_price))
200.0
Считает среднюю цену по списку.
Аргументы:
prices: список цен в рублях.
Возвращает:
среднее арифметическое по списку.Аннотация отвечает на вопрос «какие типы», docstring — «что это делает и как этим пользоваться». Вместе они дают достаточно, чтобы вызвать функцию, не открывая её: типы известны, смысл известен, а inspect.getdoc вернёт описание обычной строкой, если нужно его показать или сохранить.
Сначала предскажи ответ в голове — это главный навык программиста.
def half(n):
"""Возвращает половину числа."""
return n / 2
print(half(10))
print(half.__doc__)
def broken():
result = 1
"""Эта строка не docstring."""
return result
print(broken.__doc__)
def area(width, height):
"""Площадь прямоугольника: ширина на высоту."""
return width * height
print(area(3, 4))
print(len(area.__doc__))
1. Что такое docstring?
2. Как достать документацию функции my_func без справочной системы?
3. Строка в тройных кавычках стоит второй строкой тела, после присваивания. Что в __doc__?
4. Что делает help(функция) в терминале?
5. Чем docstring отличается от комментария #?
6. Что лежит в double.__doc__ у lambda-функции?
7. Где пишется docstring у класса?
8. Что делает doctest.testmod()?
9. Нужно ли писать в docstring то, что уже видно из аннотаций?
Напиши функцию initials с docstring: она принимает имя и фамилию и возвращает инициалы — первые буквы слов с точками. Напечатай результат и документацию.
Как посмотреть документацию функции в Python?
Напечатать атрибут __doc__: print(функция.__doc__) — это обычная строка. В терминале работает help(функция): он печатает ту же документацию красиво, через интерактивный пейджер, который закрывается клавишей q.
Урок 17: аннотации типовЧем docstring отличается от комментария?
Комментарий со решёткой объясняет код тому, кто его правит, и существует только в файле. Docstring — документация для вызывающего: Python хранит её в __doc__, её показывают help() и подсказки редактора. Пользователю функции — docstring, правящему код — комментарий.
Как написать многострочный docstring?
Открыть строку тройными кавычками сразу после def: первая строка — короткое резюме с глаголом, дальше — раздел Аргументов по строке на каждый и раздел Возврата. Для чистой печати подключают inspect.getdoc — он убирает лишние отступы.
Обязателен ли docstring для каждой функции?
Нет, атрибут __doc__ у функции без документации равен None, и код работает как работал. Но у публичной функции — той, которую будут вызывать другие или вы сами из другого файла, — docstring стоит первой строки тела по привычке: визитка дешевле вопросника.
Как посмотреть docstring, не открывая help() с пейджером?
Напечатать атрибут: print(функция.__doc__) — обычная строка без всяких страниц. Для чистого текста без отступов подойдёт inspect.getdoc(функция): результат можно сохранить в файл или показать в отчёте.
Что такое doctest и зачем он нужен?
Модуль стандартной библиотеки, который находит в docstring строки с >>> и проверяет, что примеры дают заявленный результат. doctest.testmod() возвращает счётчик вида TestResults(failed=0, attempted=1). Документация в таком случае проверяется автоматически.
Чем аннотации типов отличаются от docstring по назначению?
Аннотация отвечает на вопрос «какие типы» — её читает редактор и mypy. Docstring отвечает на вопрос «что это делает и как пользоваться» — её читает человек. Вместе они дают полную визитку функции: типы известны, смысл известен.
Понравился урок? Сошлитесь на него
«docstring — это строка в тройных кавычках сразу после def: она живёт в атрибуте __doc__ и попадает в справку help().»
Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.
Что дальше
Осталось в разделе: 20 из 20. Прогресс хранится в вашем браузере.
Что читать дальше
Функции · Урок 17
Аннотации типов в Python: подсказки для людей
Подписываем аргументы и результат: что дают аннотации, что они не проверяют, и как выглядят типы для списков, словарей и Optional.
Функции · Урок 6
Lambda в Python: функция в одну строку
lambda — правило без имени: аргументы до двоеточия, одно выражение после. Учимся писать короткие правила и передавать их в другие функции аргументом.
Функции · Урок 20
Проект на функциях Python: библиотека утилит для текста
Итоговый проект раздела: своя мини-библиотека утилит для текста — normalize, word_count, top_words. Три куска, каждый запускается и проверяется отдельно.
Похожие уроки по темам
Подобраны автоматически по пересечению тем и ключевых слов.
json · Урок 7
Вложенные структуры: списки в словарях и наоборот
Настоящие данные не плоские: список товаров внутри заказа, массив объектов в ответе API. Учимся читать вложенный JSON сверху вниз и не теряться в глубине.
json api формат pythonсписок внутри словаря python
json · Урок 19
Мини-клиент API на функциях
URL, разбор ответа, фильтр, файл — каждый шаг уже умещается в одну функцию. Собираем их в клиент, который читается как сценарий.
python клиент api функциифункции python для обработки json
Функции · Урок 12
Встроенные функции Python: инструментальный ящик
Десятки функций лежат в Python наготове: len, sum, min, max, abs, round, enumerate, zip, input. Обзор инструментального ящика — что чем открывать.
встроенные функции pythonpython input функция