Pull to refresh

Comments 14

Никаких притензий к переводчику, так как уровень моего англиского стремится к нулю, но…
Метод from_dict полезен при создании модели из данных, поступающих из другого слоя (такого как слой базы данных или из строки запроса в REST слое)

очень полезен, особенно если у меня 100500 атрибутов… как насчет распаковки аргументов в конструкторе?
storageroom_1 = StorageRoom(**kwarg)

to_serialize = {
                'code': o.code,
                'size': o.size,
                'price': o.price,
                "latitude": o.latitude,
                "longitude": o.longitude,
            }

Очень грамотно, особенно если учесть что завтра у меня появится еще одно поле, и мне его надо будет добавить (по коду автора):
— в модель,
— в сериалайзер
— anywere

Не ставлю под сомнение пользу статьи, но не уж то практика с базовыми схемами, для моделей, валидаторов, сериализаторов настолько плоха, что нужно писать такие самокаты? (прошу агрументированно проправить если я не прав)
Думаю, что распаковка аргументов в конструкторе — это здорово и питонично. Но мы тут её не видим, потому, что всякие концепции (чистого кода, DDD и прочее) описывались Робертом Мартиным, Эриком Эвансом, Мартиным Фаулером и прочими для статически типизированных языков как Java или C#. Просто опыт работы с данными методологиями ещё не обтесался и не питонизировался. Надеюсь, всё ещё впереди.
Вот в этом и проблема: нести неизменные идеи/стратегии из других языков, в частности как Вы отметили с Java/C#.
Я бы сказал, что даже в тексте чувствуется подход из другого языка, в использовании адаптеров/интерфейсов/кучей наследования и других, нужных и не очень, прокси-элементов.
В питоне нет интерфейсов (в привычном понимании) но я думаю люди не испытывают от этого какие-то значиимые неудобства (исключение быть может — те кто переучивается), зато есть другие средства которые упрощают и улучшают программирование на данном языке, и используя данные инструменты, строятся свои архитектурные особенности, свойственны именно данному языку(встроенные методы, широкое использование интроспекции, мета программирование и прочее.).

Распаковка аргументов багоопасна в проектах с длительным сроком жизни.
Быть может, не самый удачный пример:
class File:
    """
    User's data type
    """
    def __init__(self, path):
        self.path = path

    def __str__(self):
        return self.path


class Scheme():
    """
    Abstract scheme of model
    """
    _scheme = {'doo': int, 'foo': str, 'bar': File}


class Model(Scheme):

    """
    Data model
    """
    def __init__(self, **kwargs):
        self.__dict__.update(self._scheme)
        for k, v in kwargs.items():
            setattr(self, k, v)

    def __setattr__(self, key, value):
        if not hasattr(self, key):
            raise AttributeError
        elif not issubclass(type(value), self._scheme[key]):
            raise ValueError
        object.__setattr__(self, key, value)

    def serial(self):
        return {x: getattr(self, x) for x in self._scheme}


Схема, модель, валидатор сереализатор и управление данными в 40 строк кода… разве не к этому мы все стремимся — к простоте?
Это красиво и удобно. Но если на той стороне появилось еще одно поле? Конструктор получит неизвестный аргумент и все сломается? Или он это тихо проглотит и сломается в десятке других мест?

Интерпретатор выдаст исключение, о том, что передан неизвестный keyword аргумент. Если я правильно понял вопрос. Все сломается и всплывет при первом появлении

Да, в этом случае получается, что работоспособность кода полностью зависит от внешнего приложения. Например, выкатили обновление API и весь код поломался, потому что начал получать новое поле.
Если это поле нигде не учитывается в коде, то ничего нигде и не сломается. Новое поле просто будет игнорироваться.
А, к примеру, если в де/сериализаторе учитывалась контрольная сумма по полученным полям, и с новым полем у вас получаются другие суммы, то, скорее всего, неверен код сериализатора. Или же он ожидал конкретную структуру, значит, вы должны строго придерживаться её, значит, должна быть валидация получаемых данных. Приложение ругнётся, но вылетать ему необязательно.
Это вопрос EAFP vs LBYL. В Python обычно EAFP предпочтительнее. Поэтому код «кричит и валится» при первой такой возможности, чтобы сразу обратить на себя внимание.

Если источнику нет никакого доверия, а живучесть приложения нужно повысить, то тут стоит поступить уже иначе. Либо оставить вариант из статьи, либо сделать что-то такое:

class A:
    def __init__(self, a=None, b=None, *args, **kwargs):
        self.a = a
        self.b = b
        


Тогда, неизвестные аргументы при вызове A(**a_dict) будут игнорироваться без выброса исключения
Так тут же TDD, всё сломается в десятке других мест при прогоне тестов, так что, ничего страшного.
Если у вас честно все покрыто тестами, то да, вы сможете отловить эту ситуацию.
Причем надо понимать, что она может возникнуть в любой момент — даже когда вы ничего не меняли, а изменилось удаленное API и оно начало выдавать что-то еще.
Уже несколько месяцев «болею» clean architecture. Но примеров для Python было мало, а когда пытался реализовать в описанном стиле появлялось ощущение что пишу на Java. Какой тогда смысл использовать Python? В поисках решений выяснил что очень схожие идеи предлагают DDD(Domain Driven Design) и Hexagonal Architecture. Вот хорошая подборка примеров DDD для динамических языков .

Еще интересный пример который встретился https://gist.github.com/justanr/1f38e09caad47bd0d927

Cjay а Вам удалось применить данную методику на практике?

Но примером Но применить по делу так и не пришлось.
Нет, пока не применил. Пытаюсь постичь. В том числе смотрю и на DDD. Спасибо за подборку.
Sign up to leave a comment.

Articles