сеньорчикОткрыть в Telegram
← вся теориятеория к собесу · Рантайм Python

Типизация и mypy

Подсказки типов

Написал функцию, которая по подсказкам принимает два целых, и вызвал её с двумя строками. Рантайм спокойно вернул склеенную строку - ему всё равно. Прогнал тот же файл проверкой типов: три ошибки, включая обращение к методу у значения, которое может быть None. Одна и та же программа: интерпретатор молчит, проверка ругается.

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

// Формулировки: «проверяются ли аннотации в рантайме?», «зачем нужен mypy?», «что такое Optional и почему он всплывает везде?»

Что делают и чего не делают

Подсказки - это данные, приписанные к функции. Я их и достал: у функции есть словарь с типами аргументов и возврата. Интерпретатор их читает, хранит и НЕ проверяет - в этом весь смысл: язык остаётся динамическим, а проверка выносится наружу.

Проверку делает отдельный анализатор, который читает код и находит несоответствия ДО запуска. На моём примере он нашёл три ошибки, включая ту, что действительно упала бы в рантайме: обращение к методу у значения, которое может оказаться None. Заметь разницу: неверные типы аргументов рантайм проглотил молча, а вот None уронил программу - анализатор поймал оба случая заранее.

// Отсюда практическая ценность. Больше всего анализатор находит не «строка вместо числа», а именно случаи с None: функция может вернуть None, а вызывающий этого не ожидает. Это самая частая ошибка в динамическом коде, и она ловится ровно здесь, а не тестами.

подсказка типа
запись об ожидаемом типе; хранится как данные и в рантайме не проверяется
статический анализатор
читает код и находит несоответствия типов до запуска

Как внедрять в живой проект

Включить строгую проверку на большом проекте разом невозможно: она выдаст тысячи ошибок и её выключат в первый же горячий день. Рабочий порядок другой.

Начинают с мягкого режима на всём коде и с обязательной проверки только для НОВЫХ файлов. Потом включают строгость по модулям, начиная с ядра, где ошибки дороже всего. Отдельно добавляют пакеты с описаниями типов для внешних библиотек, иначе анализатор о них ничего не знает и молчит там, где мог бы помочь.

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

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

Что описывать в первую очередь

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

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

// И честная граница возможностей: анализатор не спасёт от неверной логики, не проверит данные, пришедшие из базы или по сети, и не заменит тесты. Он ловит ровно один класс ошибок - несостыковки типов, - зато делает это на всём коде и мгновенно.

границы модулей
публичные функции и интерфейсы между слоями; там подсказки полезнее всего
модель с проверкой
описание данных, которое проверяет значения и в рантайме

Как отвечать: «Проверяются ли аннотации типов во время выполнения?»

Нет. Они хранятся как данные - у функции есть словарь с типами аргументов и возврата, его можно прочитать, - но интерпретатор их не проверяет. Я специально показывал: функция, по подсказкам принимающая два целых, спокойно вернула склеенную строку при вызове с двумя строками. Проверку делает отдельный анализатор до запуска, и на том же файле он нашёл три ошибки. Самая ценная из них - обращение к методу у значения, которое может оказаться None: именно этот случай в рантайме и падает. Поэтому подсказки полезны в паре с анализатором в конвейере, а не сами по себе. Если нужна проверка данных именно в рантайме - например, того, что пришло по сети, - для этого берут модели с проверкой значений, потому что подсказки такого не делают.

Ответ отвечает прямо, показывает, где польза, и отделяет подсказки от проверки данных в рантайме. Последнее путают постоянно.

На чём валятся

  • Думают, что аннотации проверяются интерпретатором.
  • Ждут от анализатора проверки данных, пришедших извне.
  • Включают строгий режим разом на большом проекте и выключают его через неделю.
  • Не ставят проверку в конвейер, и она превращается в украшение.
  • Не добавляют описания типов для внешних библиотек и не понимают, почему проверка молчит.

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

Пять вопросов из банка по этой подтеме. Всего их 15, остальные разбираются в тренажёре.

  1. #typing_mypy1 / 5
    Что делает mypy?
    A)Статически проверяет типы по аннотациям до запуска кода
    B)Ускоряет исполнение, компилируя аннотированный код в машинный
    C)Автоматически расставляет аннотации типов по всему проекту сам
    D)Проверяет типы в рантайме, останавливая программу на несоответствии
    показать ответ и разбор
    +A)Статически проверяет типы по аннотациям до запуска кода

    // разбор: mypy — статический анализатор: читает аннотации и код и находит несоответствия типов (передали str вместо int, забыли None-ветку) до запуска, не исполняя программу. Это ловит класс ошибок на этапе разработки/CI. Он ничего не ускоряет и не меняет поведение — только проверяет.

  2. #typing_mypy2 / 5
    Что означает аннотация Optional[int]?
    A)Целое число, которое разрешено опустить только в середине кортежа
    B)Тип на выбор, аналог Any для гибкой типизации значения
    C)Необязательный аргумент, который можно вообще не передавать в вызов
    D)Значение либо int, либо None — то же, что int | None
    показать ответ и разбор
    +D)Значение либо int, либо None — то же, что int | None

    // разбор: Optional[int] — сокращение для int | None: значение либо целое, либо None. Это про тип, а не про обязательность аргумента (частая путаница): параметр с Optional-типом всё равно надо передать, если у него нет дефолта. mypy заставит обработать None-ветку, прежде чем использовать значение как int.

  3. #typing_mypy3 / 5
    Зачем вообще типизировать код на Python, если рантайм типы не проверяет?
    A)Аннотации ускоряют выполнение программы за счёт оптимизаций рантайма
    B)Без аннотаций современный Python-код попросту не запускается
    C)Ранняя ловля ошибок, подсказки IDE и живая документация
    D)Типы обязательны только для асинхронного кода, для остального нет
    показать ответ и разбор
    +C)Ранняя ловля ошибок, подсказки IDE и живая документация

    // разбор: Типы окупаются вне рантайма: mypy ловит несоответствия в CI, IDE даёт автодополнение и находит опечатки, аннотации служат живой документацией сигнатур и делают рефакторинг безопаснее. На больших кодовых базах это заметно снижает класс глупых ошибок, ничего не стоя в исполнении.

  4. #typing_mypy4 / 5
    Аннотации типов (x: int) — влияют ли они на выполнение программы?
    A)Да: аннотации ускоряют код, позволяя интерпретатору выделять память под точный тип
    B)Да: несовпадение объявленного и фактического типа немедленно вызывает исключение
    C)Да: интерпретатор проверяет соответствие типов при каждом присваивании значения
    D)Нет: в рантайме они не проверяются, это подсказки для инструментов и людей
    показать ответ и разбор
    +D)Нет: в рантайме они не проверяются, это подсказки для инструментов и людей

    // разбор: Python — язык с динамической типизацией; аннотации по умолчанию НЕ проверяются в рантайме и не влияют на выполнение. Их читают статические анализаторы (mypy, pyright), IDE (автодополнение, навигация) и люди (документация). Проверку включают отдельными инструментами до запуска. Рантайм-валидацию по аннотациям делают явно (Pydantic, typeguard), но это уже сторонние библиотеки.

  5. #typing_mypy5 / 5
    mypy подсветил ошибку типов, но программа всё равно запускается и работает. Почему?
    A)Значит, mypy ошибся: раз код запускается, то типы в нём заведомо корректны
    B)mypy — статическая проверка, она не блокирует запуск интерпретатором
    C)Программа работает лишь по случайности и упадёт при первом же вызове этой функции
    D)mypy обязан останавливать запуск при ошибке; раз не остановил — он настроен неверно
    показать ответ и разбор
    +B)mypy — статическая проверка, она не блокирует запуск интерпретатором

    // разбор: mypy проверяет типы статически, до и отдельно от исполнения; его вердикт не влияет на запуск — Python выполнит код, даже если mypy нашёл несоответствие. Ошибка типов может и не проявиться в рантайме (ветка не исполнилась), но это сигнал реального риска. Поэтому mypy ставят воротами в CI: красный отчёт не пускает merge, хотя интерпретатор код запустил бы.

дальше

Теорию прочитали. Навык ставится повторением

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