Предупреждение: Данный пост пропах субъективной оценкой и холиварными суждениями.

Вы часто задумывались про пакеты и их именование? А про именование модулей?

Или может вы все складываете в модуль utils - “и так сойдет”?

А вишенкой на торте - в один модуль запихнуть с десяток классов, процедур и методов.

Давайте вместе подумаем как сделать лучше, проще и удобней.

Для начала давайте перестать все хранить в utils.

Допустим, у нас есть в utils 3 модуля:

  • date_operations.py (как-то преобразовываем даты)
  • currency_operations.py (преобразовываем валюты)
  • service_code_converter.py (а тут какие-нибудь системные коды преобразуем в человеко-читаемое нечто)

Что можно с ними сделать? Для начала давайте разобъем на гипотетические классы, с учетом ухода от абстракций к реальным сущностям. Получим:

  • DateClass
  • CurrencyClass
  • ServiceCodesClass

Почему добавляется Class - объясню позже.

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

  • для DateClass формируем пакет date
  • для CurrencyClass формируем пакет currency
  • для ServiceCodesClass формируем пакет service_codes

Теперь, импортируя эти классы, будем получать что-то вроде:

from currency import CurrencyClass

Отлично, осталось разложить по правильным пакетам. Но как это сделать? Давайте попробуем пакет назвать по названию модуля. Получим что-то вроде:

from currency.currency import CurrencyClass

Выглядит как в песне “В доме, который построил Джек”. В целом - не очень. Но что же делать в таком случае?

А в таком случае - устанвливайте принадлежность данных модулей.

Вот, например, ServiceCodesClass - скорее всего принадлежит обработке какого-нибудь ответа от смежного сервиса.

А точнее результату его парсинга.

Отлично, тогда можно сделать так:

from message.response.service_codes import ServiceCodesClass

CurrencyClass, впрочем как и DateClass - скорее всего будут относиться к преобразованию данных в запросах/ответах:

from message.currency import CurrencyClass
from message.date import DateClass

Итого мы получили следующее:

from message.currency import CurrencyClass
from message.date import DateClass
from message.response.service_codes import ServiceCodesClass

И сравним с предыдущим вариантом:

from utils import date_operations, currency_operations, service_codes_operations

Что же нам это дало?

1) мы теперь четко знаем принадлежность конкретного класса к задаче 2) мы четко видим структуру приложения 3) мы не можем накидать абстрактных методов и процедур в utils, как в помойку

Итого, новый разработчик, придя на проект - сразу будет понимать 3 основных вещи:

  • Что это?
  • Зачем это?
  • Откуда это?

Более того, через полгода вам не придется шариться по utils, выискивая ту самую процедуру get_id или format_all.

Да, я забыл рассказать почему название класса DateClass, а не Date.

По хорошему - вы не должны возвращать примитивы, такие как bool или int. Вы должны возвращать объекты. В будущем вам будет проще добавить еще одно свойство, чем поменять примитив.

Допустим была у вас функция, которая возвращала True или False. Но тут, вам понадобилось третье состояние! А давайте быстро заменим на циферки, будет возвращать 0, 1, -1. Почему бы и нет, можем же!

Вы спешно поменяли, сделали рефактор силами idea или сами полазили по коду, нашли все использования этой функции и поменяли обработчики. Довольные выкатили на какой-нибудь слой для теста ииии…. Все сломалось. Колесо кармы дало новый оборот.

Если вы будете возвращать объект - вы можете изменить количество его аттрибутов, сделать наследника класса, формирующий объект, заоверрайдить его методы.

В итоге рядом с DateClass будет лежать DateResponse.

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

Впрочем. это тема для отдельного разговора.

Вот такие мысли сегодня ночью.

Надеюсь подискутируем в этот раз в комментариях.