Использовать Optional в абстрактных классах или нет?

Опубликовано 06 сентября 2018 в Python

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

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

    class A:
        arrt = None

    class B(a):
        arrt = 'B'

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

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

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

    class C(A):
        attr = None 

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

    class A:
        arrt = None  # type: str

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