Программирование с ИИ
ENRU
6.3

Типизация всерьёз

как заставить аннотации проверять код ИИ

Что это

Статья 2.4 объясняла, как читать аннотации типов. Здесь — как превратить их из комментария в проверку.

Сами по себе аннотации Python ничего не проверяют: напишешь -> str, вернёшь None — он промолчит. Но есть инструмент, который их читает и проверяет, и для работы с ИИ это один из самых выгодных приёмов во всей книге.

Как выглядит в коде

Код, который выглядит правильно:

def find_city(name: str) -> str | None:
    cities = {"oslo": "Осло", "bergen": "Берген"}
    return cities.get(name.lower())


def greet(name: str) -> str:
    city = find_city(name)
    return f"Привет из города {city.upper()}"

Запускается без ошибок — пока запрашивают существующий город. Проверка:

python3 -m mypy --strict mypydemo.py
измерено
mypydemo.py:8: error: Item "None" of "str | None" has no attribute "upper"  [union-attr]
Found 1 error in 1 file (checked 1 source file)

Переводится так: find_city может вернуть None, а у None нет метода upper. Ошибка найдена до запуска, без тестов, без воспроизведения, без единого пользователя.

После починки:

def greet(name: str) -> str:
    city = find_city(name)
    if city is None:
        return "Город не найден"
    return f"Привет из города {city.upper()}"
измерено
Success: no issues found in 1 source file
измерено
greet("oslo")  -> Привет из города ОСЛО
greet("paris") -> Город не найден

Почему это важно именно при работе с ИИ

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

Ошибка ИИЛовится типами
Забыл проверить Noneда
Перепутал строку и числода
Вызвал метод, которого нетда
Передал не те аргументыда
Выдумал метод библиотеки (статья 4.8)да, если у библиотеки есть типы

Это не заменяет чтения кода. Но из четырёх обязательных проверок (статья 4.1) одну теперь делает машина, каждый раз, бесплатно.

Как начать на существующем проекте

Проверять строго сразу — значит утонуть в сотнях ошибок. Разумный путь:

pip install mypy
python3 -m mypy weatherbot/          # без --strict, посмотреть масштаб

Дальше — по одному модулю: добавить аннотации, добиться чистоты, перейти к следующему. Настройки складывают в pyproject.toml:

[tool.mypy]
python_version = "3.12"
ignore_missing_imports = true

[[tool.mypy.overrides]]
module = "weatherbot.storage.*"
strict = true

Так строгость включается там, где уже навели порядок.

Как сказать ИИ

Просить типы надо явно — по умолчанию их пишут не везде:

Запрос
Добавь аннотации типов во все функции этого модуля.
Проверь, что mypy --strict проходит. Логику не меняй.

Сильный приём — попросить проверить самого себя:

Запрос
Прогони mypy --strict по своему коду мысленно: какие
ошибки он найдёт? Если найдёт — сразу исправь.

И самый полезный — типы как способ сформулировать задачу:

Запрос
Сигнатура должна быть такой:

def parse_report(raw: str) -> list[CityTemp]:

где CityTemp — dataclass с полями name: str и temp: float | None.
Реализацию пиши под эту сигнатуру.

Так типы становятся частью постановки задачи (статья 3.4): они точнее слов и проверяются машиной.

На что смотреть

Аннотации есть, mypy не запускался. Самый частый случай: типы написаны для красоты и давно разошлись с кодом.

Any повсюду. def handle(data: Any) -> Any — аннотация, которая ничего не обещает. ИИ ставит Any, когда не уверен; это место стоит уточнить.

# type: ignore без объяснения. Способ заглушить проверку. Иногда оправдан, но комментарий должен говорить почему.

-> None у функции, которая что-то возвращает. Аннотации не проверяются при запуске, и такое расхождение живёт годами.

Типы только в сигнатурах, без --strict. Без строгого режима mypy пропускает нетипизированные функции целиком — проверка оказывается видимостью.

Шпаргалка

  • Аннотации сами по себе ничего не проверяют, нужен mypy.

  • python3 -m mypy --strict файл.py — одна команда, ловит целый класс ошибок ИИ.

  • Самое ценное: забытая проверка на None находится до запуска.

  • На готовом проекте включай строгость по модулям, а не сразу везде.

  • Any и # type: ignore — места, где проверка отключена.

  • Сигнатура в запросе — самый точный способ поставить задачу.