Стандарты и сертификаты · тема 3
6

Документация в коде:
PEP 257, 484 и 20

Кроме оформления, у кода на Python есть стандарты описания: как писать строки документации, как указывать типы и какими принципами руководствоваться. В главе разобраны PEP 257, PEP 484 и PEP 20 с примерами на одном модуле.

Документы
PEP 257 · PEP 484 · PEP 526 · PEP 604 · PEP 20
Уровень
Базовый
Где встречается
Библиотеки, сервисы с командой больше одного человека
Зачем это знать
Чтобы функцию можно было использовать, не читая её тело

§1Строки документации по PEP 257

Строка документации (docstring) — строковый литерал первой строкой модуля, класса или функции. Python сохраняет его в атрибуте __doc__, его показывают help(), подсказки редактора и генераторы документации.

PEP 257 задаёт форму: тройные двойные кавычки; первая строка — краткое описание действия в форме повеления, с точкой в конце; если нужно больше, после неё пустая строка и подробности.

Однострочная строка документации

так не надо
def age(birth_date):
    '''
    this function returns age
    '''
так надо
def age(birth_date):
    """Вернуть полное число лет на сегодняшний день."""

Слева кавычки одинарные, текст разнесён на три строки и описывает функцию в третьем лице. Справа одна строка-команда: «Вернуть…», как требует PEP 257.

Повторять заголовок не нужно

так не надо
def load_profile(db, user_id):
    """load_profile(db, user_id) -> dict"""
так надо
def load_profile(db, user_id):
    """Прочитать профиль ученика из базы по его идентификатору."""

Параметры и так видны в заголовке функции. Строка документации сообщает её назначение.

многострочная строка документации, стиль Google
def send_report(users, report_date, *, dry_run=False):
    """Отправить отчёт об активных пользователях за день.

    Пользователи без адреса почты в отчёт не попадают.

    Args:
        users: Список словарей с ключами name, email, active.
        report_date: День, за который собран отчёт.
        dry_run: Если True, отчёт собирается, но не отправляется.

    Returns:
        Код ответа сервиса отчётов или None при ошибке сети.

    Raises:
        ValueError: Если report_date в будущем.
    """

PEP 257 не задаёт, как описывать параметры. Для этого есть соглашения: стиль Google (разделы Args, Returns, Raises), стиль NumPy (подчёркнутые заголовки) и reStructuredText (:param users:). В проекте выбирают одно.

PEP 257 «Docstring Conventions».
PEP 257 «Docstring Conventions». Документ короткий: однострочные и многострочные строки документации, отступы, строки модуля и класса.

§2Аннотации типов по PEP 484

Аннотация типа — пометка, какого типа ожидается параметр, переменная или результат. Синтаксис ввёл PEP 3107, смысл аннотаций для типов — PEP 484, аннотации переменных — PEP 526, запись int | None — PEP 604.

Интерпретатор аннотации не проверяет: age("вчера") выполнится и упадёт внутри. Проверяет отдельная программа — mypy или pyright, до запуска.

Функция без аннотаций и с ними

так не надо
def find_ticket(tickets, ticket_id):
    for t in tickets:
        if t.id == ticket_id:
            return t
так надо
def find_ticket(tickets: list[Ticket], ticket_id: int) -> Ticket | None:
    for ticket in tickets:
        if ticket.id == ticket_id:
            return ticket
    return None

Справа по заголовку видно, что функция может ничего не найти. mypy потребует проверить результат на None перед обращением к его полям.

что сообщает mypy
$ mypy tickets.py
tickets.py:14: error: Item "None" of "Ticket | None" has no attribute "title"  [union-attr]
Found 1 error in 1 file (checked 1 source file)
ЗаписьСмысл
x: int = 0переменная целого типа, PEP 526
list[str]список строк, начиная с Python 3.9
dict[str, int]словарь со строковыми ключами и целыми значениями
int | Noneцелое или None, начиная с Python 3.10
Callable[[int], str]функция, принимающая int и возвращающая str
-> Noneфункция ничего не возвращает
PEP 484 «Type Hints».
PEP 484 «Type Hints». Документ описывает смысл аннотаций для типов; синтаксис для переменных добавлен позже, в PEP 526.

§3«Дзен Python», PEP 20

PEP 20 — 19 коротких принципов, на которых построен язык. Их выводит команда import this. Линтер их не проверяет; на них ссылаются, когда решают, какой из вариантов кода выбрать.

import this, первые строки
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Errors should never pass silently.
Unless explicitly silenced.

«Явное лучше неявного» на примере

так не надо
from config import *

connect(HOST, PORT)
так надо
from config import DB_HOST, DB_PORT

connect(DB_HOST, DB_PORT)

Справа читатель видит, откуда взялись значения. Тот же довод стоит за запретом импорта со звёздочкой в PEP 8.

«Ошибки не должны проходить молча»

так не надо
try:
    send_email(user)
except Exception:
    pass
так надо
try:
    send_email(user)
except SMTPException:
    log.warning('письмо для %s не отправлено', user.id)
    raise

Слева сбой отправки исчезает без следа. Справа он записан в журнал и передан выше.

PEP 20 «The Zen of Python».
PEP 20 «The Zen of Python». Автор — Тим Питерс. В тексте PEP сказано, что принципов двадцать, а записано девятнадцать.

§4Частые ошибки

Описывать в строке документации сигнатуруПараметры видны в заголовке; строка сообщает назначение.
Писать строку документации в третьем лицеPEP 257 требует повеление: «Вернуть…», «Отправить…».
Думать, что аннотации проверяются при запускеПроверяют их mypy или pyright до запуска.
Возвращать None без отметки в аннотацииПишут Ticket | None, и проверка заставит обработать отсутствие.
Смешивать стили описания параметровВ проекте выбирают Google, NumPy или reStructuredText и держатся одного.

§5Проверьте себя

  1. Где Python хранит строку документации и кто её показывает?
  2. Как выглядит однострочная строка документации по PEP 257? Приведите пример.
  3. Зачем в аннотации писать Ticket | None, если функция может ничего не найти?
  4. Проверяет ли интерпретатор аннотации типов при запуске? Чем их проверяют?
  5. Какие принципы PEP 20 объясняют запрет импорта со звёздочкой и пустого except?
PEP 257"""тройные двойные"""повеление, точкапустая строка после краткойArgs, Returns, Raises
ТипыPEP 484 — смыслPEP 526 — переменныеPEP 604 — int | Nonemypy, pyright
PEP 20import thisявное лучше неявногоошибки не молчатчитаемость важна

Справочник «Стандарты и сертификаты» · глава 6Макаров Максим Николаевич

Кадр