Подсказки типов в Python необязательны: вы вольны выбирать, пользоваться ими или нет. Но, начав включать их в свой код, вы точно столкнётесь с определёнными трудностями аннотирования функций или переменных. Эта статья — моя точка зрения на один специфический случай.

К примеру, имеется иерархия классов, которая выглядит похожей на ту, что представлена в коде ниже. Это может быть какой‑то маппинг в базу или иерархия команд. Самая важная часть в этом примере — то, что имеется один абстрактный класс и несколько реальных классов, его наследников. Не так важно, помечен ли этот абстрактный класс специальным образом или нет. Я просто предполагаю, что он используется в качестве такового.

class A:
    attr = None

class B(A):
    attr = 'B'

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

Есть два варианта добавления подсказки по типам (кроме Any, конечно): использовать Optional или нет. Давайте обсудим первый вариант.

class A:
    attr = None  # type: Optional[str]

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

class C(A):
    attr = None

Но это не то поведение, которое требовалось. Нашей целью было избежать этого.

Второй вариант — без Optional — работает намного лучше.

class A:
    attr = None  # type: str

Если описать типы так, то класс C не пройдёт проверку mypy — как раз необходимый нам результат. Подсказки типов в Python — вещь дополнительная. Динамическая природа и легаси‑код время от времени вызывают проблемы. Optional иногда — хороший выбор, но не всегда.