Подсказки типов в 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 иногда — хороший выбор, но не всегда.