Почему? Decoupling со всеми вытекающими обстоятельствами: гибкость, тестирование и т. д.
2. Указывать зависимости явно — тоже хорошо.
Приведу примеры от обратного.
Вариант №1. В Dependency Injector можно реализовать метод container.autowire(). Это метод сможет с высокой точностью связать все компоненты приложения без вас. Контейнер будет выглядеть следующим образов:
Много ли можно сказать о приложении по такому контейнеру? Почти ничего.
Вариант №2. Отказаться от наличия контейнера и добавить магию в стиле декоратора @inject:
from dependency_injector import inject
@inject
class Dispatcher:
def __init__(self, monitors: List[Monitor]) -> None:
...
@inject
class HttpMonitor(Monitor):
def __init__(
self,
http_client: HttpClient,
options: Dict[str, Any],
) -> None:
...
В таком случае ваш код будет прочно привязан к dependency injection фреймворку. Как подключать сторонние модули..? Как собрать один и тот же класс с разными параметрами (пример с HttpMonitor из данного примера)..? Для простых случаев удобно, в более сложных — фреймворк наложит прилично ограничений.
В первой версии Dependency Injector декоратор @inject был. Убрал его специально. Считаю, что код должен жить отдельно от фреймворка. Это главный принцип Dependency Injector. Он не загрязняет ваш код магическими декораторами и правилами. Вместо этого он накладывается поверх на любой код, написанный по принципу dependency injection.
Про pytest
Фикстуры в pytest — удобная штука. Зависимости там тоже указываются явно. Если переписать контейнер из этого примера, то получится вот так:
Явное лучше неявного — это классный принцип. Он здесь полностью реализован.
1) Самостоятельно определять порядок создания обьектов. Ваш фреймворк этого не делает и требует явного описания порядка
Это как раз про «явное лучше неявного». В Python многие библиотеки при инициализации основных классов используют *args, **kwargs + документацию. Интроспекцию на этом не построишь. Отказался от этого в Dependency Injector специально.
2) Управлять временем жизни обьекта. Условно есть обьект представляющий собой транзакцию в БД время жизни которого — запрос, есть обьект соединениу к базе данных, который живет постоянно. В таком случае DI фреймворк управлял бы выделением соединений из пула и созданием временных обьектов и т.д. Ваш фреймворк насколько я понял такого тоже не умеет.
Из коробки умеет базовые вещи в плане управления временем жизни: Singleton, ThreadLocalSingleton, ThreadSafeSingleton.
Кому удобнее? Вам как автору фреймворка или вчерашнему джуну который его первый раз видит?
Все когда-то все видели в первый раз. Вопрос популярности. Dependency Injector не новый фреймворк. Его скачивают с PyPi 200 тыс. раз в месяц.
В чем профит описания структуры без создания объектов? Для того чтобы понять структуру приложения разработчик читает его код, в процессе чтения кода объекты не создаются. В процессе работы приложения обьекты все равно будут созданы и без разницы произойдет это в функции create_app или в контейнере.
сделать_что-то_0()
Объект1
сделать_что-то_1()
Объект2
- зависит от:
- Объект1
Для меня стало очевидно, что удобнее разделять. «Можно ли написать все в одном файле?», «Можно ли в html шаблоне написать sql запрос?» — можно, но лучше так не делать.
1) все объекты создаются на этапе инициализации, т.е. это абсолютно тоже самое что руками написать код создания объектов.
Объекты на этапе инициализации не создаются. ApplicationContainer декларативный, при его описании не создается ни одного объекта.
2) все эти объекты в общем случае существуют все время выполнения программы и соответственно ApplicationContainer никак не управляет их временем жизни.
ApplicationContainer управляет временем жизни. Фабрики создают объекты при обращении к ним и передают зависимости в создаваемый объект. Если зависимость — другая фабрика, она тоже создаст объект. Таким образом соединяя провайдеры можно описывать сложные графы. В модуле providers есть другие провайдеры Singleton, ThreadLocalSingleton, ThreadSafeSingleton, и т. д.
абсолютно непонятно зачем вообще это все нужно и какую вообще проблему решает DI
Это классный вопрос. Специально его не затрагивал. Хотел сделать практическое руководство.
Про пользу dependency injection. Я относился к нему пренебрежительно пока в 2014 на себе не почувствовал его магическую силу. Это случилось при рефакторинге крупной легаси платформы. Было много запутанного кода. Мы применили DI. Когда применяешь DI все связи становятся явными. Если когда-то пробовали вышивать, это как заглянуть на обратную сторону рисунка. Структура приложения вырисовывается сама собой. Она отделяется от runtime части и с ней становится удобно работать. После применения DI все стало прозрачно и просто. В тот момент я осознал силу подхода.
Так и появился Dependency Injector.
Если интересно, напишу отдельную статью.
PS: Подход неинтуитивный, нужно привыкнуть. Плюс это инвестиция в будущее: когда пишешь приложение и добавляешь все в контейнер — кажется избыточно. Когда возвращаешься через время чтобы что-то поменять — рад тому, что контейнер есть.
что мешает сделать тоже самое в функции create_app и без всякого фреймворка? Результат будет одинаковый.
Мой фреймворк позволяет описать всю структуру приложения в декларативном стиле не создав ни одного объекта. В таком виде со структурой удобнее всего работать.
Тоже самое можно сделать и без него, только у вас получится такой же фреймворк. Его нужно будет протестировать, оптимизировать, настроить сборку 44 wheels для всех версий Python и ОС, поддерживать, фиксить баги, написать документацию, примеры и это руководство. Для моего фреймворка я это все уже сделал.
Писать все в функцию create_app() неправильно. Это смешивание декларативной и runtime частей. Такие приложения тяжелее понимать.
Модуль containers — Python код, который транслирован в C c помощью Cython. Есть несколько особенностей: несколько mapping'ов на модуль providers, несколько cdef приведений типов и несколько cpdef функций.
В планах перевести модуль containers в более глубокую Cython типизацию после прекращения поддержки Python 2. Пока не получается из-за применения метакласса.
Скорее всего такое представление (управленец — человек первого сорта, инженер — второго) складывается из-за того, что большинство людей не видят развитие себя в техническом плане. И получается, что вместо хорошего системного архитектора появляется плохой тим лид.
Скажите, а как обстоят у вас дела с контролем доступа?
Т. е., если я хочу показывать видео только тем, кому разрешает моя бизнес логика. Вариант с секретными ссылками не рассматриваем. Хотелось бы сделать честную проверку, а именно, перед началом показа видео спросить мою систему, можно ли вот этому пользователю (нужные его куки) предоставить права на просмотр вещания?
А я считаю, что как раз таки оверинжининг встречается очень часто, и это большое зло. Он создает излишнюю неоправданную сложность. Что касается поста, то этот код спокойно можно переписать в структурном стиле, его суть не изменится.
«Complex is better than complicated.»
Я придерживаюсь явного декларативного подхода.
Тут используется несколько контейнеров.
Сборка выглядит вот так:
Это немного устаревший вариант. Для такой сборки сейчас есть специальный провайдер
Container
. Он автоматизирует то, что написано в__init__()
.Проект находится тут — Newsfeed.
Dependency Injector — это про 2 вещи:
1. Dependency injection — это хорошо.
Почему? Decoupling со всеми вытекающими обстоятельствами: гибкость, тестирование и т. д.
2. Указывать зависимости явно — тоже хорошо.
Приведу примеры от обратного.
Вариант №1. В Dependency Injector можно реализовать метод
container.autowire()
. Это метод сможет с высокой точностью связать все компоненты приложения без вас. Контейнер будет выглядеть следующим образов:Много ли можно сказать о приложении по такому контейнеру? Почти ничего.
Вариант №2. Отказаться от наличия контейнера и добавить магию в стиле декоратора
@inject
:В таком случае ваш код будет прочно привязан к dependency injection фреймворку. Как подключать сторонние модули..? Как собрать один и тот же класс с разными параметрами (пример с HttpMonitor из данного примера)..? Для простых случаев удобно, в более сложных — фреймворк наложит прилично ограничений.
В первой версии Dependency Injector декоратор
@inject
был. Убрал его специально. Считаю, что код должен жить отдельно от фреймворка. Это главный принцип Dependency Injector. Он не загрязняет ваш код магическими декораторами и правилами. Вместо этого он накладывается поверх на любой код, написанный по принципу dependency injection.Про pytest
Фикстуры в pytest — удобная штука. Зависимости там тоже указываются явно. Если переписать контейнер из этого примера, то получится вот так:
В целом тоже самое, но писать больше.
Это как раз про «явное лучше неявного». В Python многие библиотеки при инициализации основных классов используют
*args
,**kwargs
+ документацию. Интроспекцию на этом не построишь. Отказался от этого в Dependency Injector специально.Из коробки умеет базовые вещи в плане управления временем жизни:
Singleton
,ThreadLocalSingleton
,ThreadSafeSingleton
.Еще из коробки умеет собирать сложные графы объектов и добавлять некоторую вариативность на базе конфигурации.
Все когда-то все видели в первый раз. Вопрос популярности. Dependency Injector не новый фреймворк. Его скачивают с PyPi 200 тыс. раз в месяц.
На мой взгляд удобнее читать такой код:
чем такой:
Для меня стало очевидно, что удобнее разделять. «Можно ли написать все в одном файле?», «Можно ли в html шаблоне написать sql запрос?» — можно, но лучше так не делать.
Это руководство о том, как построить приложение используя модуль asyncio и применяя принцип dependency injection.
Dependency Injector не привязан к asyncio\flask. Его можно использовать отдельно.
Объекты на этапе инициализации не создаются.
ApplicationContainer
декларативный, при его описании не создается ни одного объекта.ApplicationContainer
управляет временем жизни. Фабрики создают объекты при обращении к ним и передают зависимости в создаваемый объект. Если зависимость — другая фабрика, она тоже создаст объект. Таким образом соединяя провайдеры можно описывать сложные графы. В модулеproviders
есть другие провайдерыSingleton
,ThreadLocalSingleton
,ThreadSafeSingleton
, и т. д.Это классный вопрос. Специально его не затрагивал. Хотел сделать практическое руководство.
Про пользу dependency injection. Я относился к нему пренебрежительно пока в 2014 на себе не почувствовал его магическую силу. Это случилось при рефакторинге крупной легаси платформы. Было много запутанного кода. Мы применили DI. Когда применяешь DI все связи становятся явными. Если когда-то пробовали вышивать, это как заглянуть на обратную сторону рисунка. Структура приложения вырисовывается сама собой. Она отделяется от runtime части и с ней становится удобно работать. После применения DI все стало прозрачно и просто. В тот момент я осознал силу подхода.
Так и появился Dependency Injector.
Если интересно, напишу отдельную статью.
PS: Подход неинтуитивный, нужно привыкнуть. Плюс это инвестиция в будущее: когда пишешь приложение и добавляешь все в контейнер — кажется избыточно. Когда возвращаешься через время чтобы что-то поменять — рад тому, что контейнер есть.
Мой фреймворк позволяет описать всю структуру приложения в декларативном стиле не создав ни одного объекта. В таком виде со структурой удобнее всего работать.
Тоже самое можно сделать и без него, только у вас получится такой же фреймворк. Его нужно будет протестировать, оптимизировать, настроить сборку 44 wheels для всех версий Python и ОС, поддерживать, фиксить баги, написать документацию, примеры и это руководство. Для моего фреймворка я это все уже сделал.
Писать все в функцию
create_app()
неправильно. Это смешивание декларативной и runtime частей. Такие приложения тяжелее понимать.Модуль
containers
— Python код, который транслирован в C c помощью Cython. Есть несколько особенностей: несколько mapping'ов на модульproviders
, несколько cdef приведений типов и несколькоcpdef
функций.В планах перевести модуль
containers
в более глубокую Cython типизацию после прекращения поддержки Python 2. Пока не получается из-за применения метакласса.Скорее всего такое представление (управленец — человек первого сорта, инженер — второго) складывается из-за того, что большинство людей не видят развитие себя в техническом плане. И получается, что вместо хорошего системного архитектора появляется плохой тим лид.
Т. е., если я хочу показывать видео только тем, кому разрешает моя бизнес логика. Вариант с секретными ссылками не рассматриваем. Хотелось бы сделать честную проверку, а именно, перед началом показа видео спросить мою систему, можно ли вот этому пользователю (нужные его куки) предоставить права на просмотр вещания?