Pull to refresh

Comments 22

Примеры неубедительные, что и понятно, потому что они слишком простые. И если они простые, нет нужды упарываться по паттернам, это частая ошибка.
Ок, допустим, у нас большая сложная инициализация, которую мы хотим упростить. Ну, я бы предпочёл простые поля развернуть как обычно, а объекты передать через Dependency Injection - пока не встречал кода, где этого было бы недостаточно.

Можете пример короткий привести?

Пример чего? Dependency injection?
Вместо

class Backend:
  def __init__(self, config):
    self.adapter = DbAdapter(user=config["user"], password=config["password"])

def main():
  ...
  backend = Backend(config)
  ...

Делаем так

class Backend:
  def __init__(self, adapter):
    self.adapter = adapter

def main():
  ...
  adapter = DbAdapter(config["uri"], config["password"])
  backend = Backend(adapter)
  ...

Я лично даже стараюсь не использовать конструкцию DbAdapter(config["uri"], config["password"]). Для каждой сущности своя секция в конфиге: DbAdapter(config["database"]). Или DbAdapter(**config["database"]) для самоуверенных.

В чем разница: передавать config или dbadapter?

Длинно - это соответствует принципам Single Responsibility и Dependency Inversion. Бэкенд не должен заниматься настройкой адаптера к базе и вообще не должен знать о том, что адаптер реально собой представляет.
Коротко - разгружаем init, проще расширяться, проще делать мок-тесты - не возимся с флагами внутри Backend, а просто передаём туда MockAdapter для тестов.

Я его как-то случайно переизобрёл когда из ФП в Питон вернулся. Потом уже вспомнил, что в SOLID'е чёт такое было. В ФП просто это совершенно естественный ход вещей.

__init__ который вернул None? Что-то подозрительное.

А разве возвращаемое значение этого метода как-то используется? Он же не вызывается явно.

PEP-0484 рекомендует в аннотации типа метода __init__ указывать None. Вероятно, для совместимости.

В питоне отсутствие return эквивалентно return None. Если мы аннотируем функции, то возращаемый тип тоже будет type(None) -> None

Логично. Я почему-то думал оно неявно инстанс класса возвращает как прочие конструкторы. Но оно же и не конструктор по сути.

Инстанс класса возвращает __new__. А __init__ вызывается уже после него.

Он инициализирует, в делает и возвращает инстанс __new__

Все верно - __init__ сам по себе не занимается созданием объекта, это делает __new__

__init__  и должен возвращатьNone иначе будет возбуждено исключение

Python — объектно-ориентированный язык. Способ создания нового объекта обычно определяется в специальном методе init, реализованном в классе.

И ведь не смущает автора поста тот факт, что сигнатура метода __ init __ первым аргументом имеет self. Не видно противоречия?

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

Вы это серьезно? Вы не знаете, что у датаклассов есть frozen? Ваши советы откровенно все какие-то наивные

> @classmethod
def from_file(cls, filepath: str) -> Configuration:


Это не будет работать, проверьте

Это не будет работать, проверьте

Почему?

В таком виде потому что не найдет Configuration как тип возвращаемого значения.

Нужно указывать Self в 3.11+ и имя класса в кавычках как строку если 3.10 и ниже -> "Configuration":

Статью можно свести к тому, что вместо

class Configuration:
    def __init__(self, filepath):
        self.filepath = filepath
        self._initialize()

следует использовать

class Configuration:
    def __init__(self, filepath=None):
        self.filepath = filepath
        if filepath:
            self._initialize_with_filepath()

Но это меняет контракт класса, и теперь возможно сделать конфигурацию без пути.

Контракт класса определяется архитектором, если он считает, что конфигурация без пути не должна существовать, то значит так оно и есть. А вариант "класс может иметь любое внутреннее состояние" (в т.ч. недопустимое с точки зрения бизнес логики) приводит потом к трудноуловимым ошибкам в дебаге.
Например, мы заложились на то, что путь у конфигурации всегда есть, а потом словим NoneType error при попытке по этому пути обратиться.
Или же потом присвоим объекту filepath, забыв сделать инициализацию.
Получится глупо и больно.
Так что не надо такой категоричности.

Тезис "логика создания объектов Python превращается в непонятное чудище" считаю слишком преувеличиваюшим.

init - лишь точка входа при создании объекта. Что скрывает вложенный туда метод или функция - не суть важно, пока все под контролем.

Динамическая типизация, *args *kwargs, инит в который можно засунуть что угодно - это инструменты.

Если конкретно вам что-то не нравится в организации кода в вашем проекте - продвигайте идею интеграции линтеров, удобных вам.

Sign up to leave a comment.