python
July 26, 2021

Какие типы возвращать и принимать

Испрользуйте ровно тот тип, который на самом деле имеется в виду. Код должен четко выражать ваши намерения и ожидания. Не давайте слишком много гарантий на параметры и возвращаемые значения. Много гарантий — больше проблем для вас как автора функции и больше вероятность изменения интерфейса.

На большинствро случаев жизни есть специальные типы контейнеров и отображений (mapping). Пример из Питона. Небольшое количество элементов — Collection. То же, но где порядок имеет смысл и нужно уметь обращаться по индексу — Sequence. Что-то длинное, по чему разрешается только один раз проитерироваться — Iterable. Нужно по одному значению получать другое — Mapping. Это относится как к возвращаемым типам, так и к типам параметров. Даже если текущая реализация функции возвращает конкретно список, надо указывать как к этому списку нужно относиться: важен ли в нем порядок, можно ли по нему много раз пройтись. Даже если текущая реализация функции проходит по контейнеру в цикле один раз, надо в аннотации указывать то, чем является параметр по сути и какие гарантии вы как автор можете дать на будущее.

Есть также изменяемые (mutable) типы контейнеров и отображений. Если вам на самом деле хочется менять параметр или, тем более, указывать, что можно менять возвращаемое значение, подумайте как следует, стоит ли оно того. Изменения состояния должны очень хорошо контролироваться. В идеале состояние должно быть инкапсулировано в класс, и любые изменения должны производиться через его методы. Но это тема для другой статьи.

В функции параметр — контравариантный тип, а возвращаемое значение — ковариантный. Более частный тип параметра означает более общий тип функции. Если функция имеет более общий тип параметра, она подходит туда, где требуется функция с более частным типом параметра. Это и есть контравариантность. С возвращаемым типом все прямее: более частный возвращаемый тип — более частный тип функции.

Нет никаких правил вроде "возвращайте наиболее частный тип, а принимайте наиболее общий". Откуда это пошло? Это всего лишь вежливость разработчика библиотеки. Это способ сделать функцию наиболее универсальной для клиентского кода, дать максимум гарантий.

Мало гарантий — неудобно клиентскому коду. Много гарантий — неудобно библиотеке. Гарантии надо выбирать раузмно.