Комментарии 14
Никаких притензий к переводчику, так как уровень моего англиского стремится к нулю, но…
очень полезен, особенно если у меня 100500 атрибутов… как насчет распаковки аргументов в конструкторе?
Очень грамотно, особенно если учесть что завтра у меня появится еще одно поле, и мне его надо будет добавить (по коду автора):
— в модель,
— в сериалайзер
— anywere
Не ставлю под сомнение пользу статьи, но не уж то практика с базовыми схемами, для моделей, валидаторов, сериализаторов настолько плоха, что нужно писать такие самокаты? (прошу агрументированно проправить если я не прав)
Метод 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#.
Я бы сказал, что даже в тексте чувствуется подход из другого языка, в использовании адаптеров/интерфейсов/кучей наследования и других, нужных и не очень, прокси-элементов.
В питоне нет интерфейсов (в привычном понимании) но я думаю люди не испытывают от этого какие-то значиимые неудобства (исключение быть может — те кто переучивается), зато есть другие средства которые упрощают и улучшают программирование на данном языке, и используя данные инструменты, строятся свои архитектурные особенности, свойственны именно данному языку(встроенные методы, широкое использование интроспекции, мета программирование и прочее.).
Я бы сказал, что даже в тексте чувствуется подход из другого языка, в использовании адаптеров/интерфейсов/кучей наследования и других, нужных и не очень, прокси-элементов.
В питоне нет интерфейсов (в привычном понимании) но я думаю люди не испытывают от этого какие-то значиимые неудобства (исключение быть может — те кто переучивается), зато есть другие средства которые упрощают и улучшают программирование на данном языке, и используя данные инструменты, строятся свои архитектурные особенности, свойственны именно данному языку(встроенные методы, широкое использование интроспекции, мета программирование и прочее.).
Распаковка аргументов багоопасна в проектах с длительным сроком жизни.
Быть может, не самый удачный пример:
Схема, модель, валидатор сереализатор и управление данными в 40 строк кода… разве не к этому мы все стремимся — к простоте?
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 предпочтительнее. Поэтому код «кричит и валится» при первой такой возможности, чтобы сразу обратить на себя внимание.
Если источнику нет никакого доверия, а живучесть приложения нужно повысить, то тут стоит поступить уже иначе. Либо оставить вариант из статьи, либо сделать что-то такое:
Тогда, неизвестные аргументы при вызове A(**a_dict) будут игнорироваться без выброса исключения
Если источнику нет никакого доверия, а живучесть приложения нужно повысить, то тут стоит поступить уже иначе. Либо оставить вариант из статьи, либо сделать что-то такое:
class A:
def __init__(self, a=None, b=None, *args, **kwargs):
self.a = a
self.b = b
Тогда, неизвестные аргументы при вызове A(**a_dict) будут игнорироваться без выброса исключения
Так тут же TDD, всё сломается в десятке других мест при прогоне тестов, так что, ничего страшного.
Уже несколько месяцев «болею» clean architecture. Но примеров для Python было мало, а когда пытался реализовать в описанном стиле появлялось ощущение что пишу на Java. Какой тогда смысл использовать Python? В поисках решений выяснил что очень схожие идеи предлагают DDD(Domain Driven Design) и Hexagonal Architecture. Вот хорошая подборка примеров DDD для динамических языков .
Еще интересный пример который встретился https://gist.github.com/justanr/1f38e09caad47bd0d927
Cjay а Вам удалось применить данную методику на практике?
Но примером Но применить по делу так и не пришлось.
Еще интересный пример который встретился https://gist.github.com/justanr/1f38e09caad47bd0d927
Cjay а Вам удалось применить данную методику на практике?
Но примером Но применить по делу так и не пришлось.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Чистая архитектура в Python: пошаговая демонстрация. Часть 2