Pull to refresh

Comments 27

Лично мне Dependency Injection не кажется ни более понятным, ни требующим меньше кода.

Чем DI лучше Factory?

Согласен. А можете показать какую-нибудь демку на фабриках? Сейчас копаю в эту сторону и изучаю как это делают другие.

UFO just landed and posted this here

DI есть где угодно. Это же просто концепция, по которой класс обозначает, какие интерфейсы ему нужны для работы, но не пытается самостоятельно это всё "раздобыть". Удовлетворением этих требований занимается пользователь класса.

В статье рассказано про IoC-контейнер, наиболее типичную реализацию DI.

UFO just landed and posted this here

Единственный "нормальный DI" - это передача зависимостей при создании экземпляра. Как именно вы будете создавать эти заивисимости - это не DI, это как раз фабрики о которых спросил человек. Их можно писать вручную, можно использовать IoC-контейнеры. Важно так же понимать, что при неправильном использовании IoC контейнера можно вообще лишиться DI как такового (например, используя глобальный экземпляр контейнера по аналогии с сервис локаторами)

Если мне кто-то покажет нормальный DI в питоне

Где-то в глубине этой статьи приводится целых 3 ссылки на реализацию IoC-контейнера (две посторонние и сабж). Вы изволили их попробовать в деле и нашли неподходящими?

Осмелюсь предположить, что dj модно реализовать везде, где есть понятие функции, принимающей параметры.

Def write(mess): prunt(mess)

Def log(mess, writer=write): writer(mess)

...

Log('hello)

DI ничем не лучше фабрики. Это перпендикулярные вещи. Важно не путать концепцию Dependency Injection и DI-фреймворки aka IoC-контейнеры (один из которых описан в статье), которые по факту и являются инструментами для упрощения создания фабрик

Просто пример диковпт. Идея иллюстрируется гораздо проще, а тут статье не столько про инъекции, сколько про какую-то конкретную либу :-)

UFO just landed and posted this here

На эту тему есть интересная библиотека с подробной документацией https://pypi.org/project/dependency-injector/. Внедрили в нескольких проектах в FastAPI - пользоваться достаточно удобно.

А чем вам не хватило встроенного в fastapi `Depends`?

Изначально был Aiohttp + DI, за счет DI легко переехали на FastAPI. DI выигрывает по гибкости у штатного Depends, да к тому же неплохо с ним интегрируется.

Из-за того что часть фреймворка написана на C - отладка ошибок в коде связанных с фреймкорком превращается в угадайку. Использовали его в проекте и крайне разочарованы.

Дочитал до первого "локального" импорта:

from .chat import AnswerGenerator

И сразу закрыл. Не понимаю чему может научить подобная статья. Вообще в целом, на мой взгляд, внедрение зависимостей в таких языках как Python или Ruby не приносит ничего полезного, ИМХО, это антипаттерн. Я например прекрасно обхожусь без всего этого используя функциональную композицию, используя классы по минимуму, и все прекрасно.

Расскажите подробнее, почему DI - это антипаттерн. Интересно сравнить с мнением Роберта Мартина и других людей

Расскажите подробнее, почему DI - это антипаттерн.

Будьте любезны, из контекста не выдирайте отдельные слова. Я же говорил:

на мой взгляд, внедрение зависимостей в таких языках как Python или Ruby не приносит ничего полезного, ИМХО, это антипаттерн.

Вот несколько мнений, может ингода пафосных, но доходчивых почему при смене парадигмы, DI превращается в костыль:

- Про Haskell
- Про F# (более детальная рефлексия)

В ФП нет классов, есть только функции, и механизмы работы с ними, каррирование, композиция, partial application, итд. Все это с лихвой и более заменяет DI на Python, Ruby, TypeScript, etc.

И все таки раскройте мысль почему внедрение зависимостей в Python не приносить пользы и атипаттерн по вашему мнению.

А разговаривая про ФП, я не понимаю вашего противопоставления каррирования и DI, по факту это тот же механизм, но примененный функциям, а не объектам.

А разговаривая про ФП, я не понимаю вашего противопоставления
каррирования и DI, по факту это тот же механизм, но примененный
функциям, а не объектам.

А вот и нет, это довольно разные концепции и механизмы.

И все таки раскройте мысль почему внедрение зависимостей в Python не приносить пользы и атипаттерн по вашему мнению.

Питон можно использовать в двух парадигмах. ФП, хоть это и не так удобно и практично как в Haskell, Clojure или F#. Если вы выбираете ФП, то вам и внедрение зависимостей не нужно и выступает как тормоз.

Да и в ООП на Питоне, на мой взгляд, это на любителя.

Погодите. Вот допустим у вас есть функция foo, которая вызывает функцию bar. Вы можете сделать это напрямую или передавать bar как аргумент. Тогда вы можете сделать foo1 = partial(foo, bar) - и вот у вас внедрение зависимости в функциональном стиле.

На мой вопрос вы так и не ответили: чем DI мешает или не помогает? Его задача одна - сделать сцепление кода ниже и тем самым изолировать слои абстракции и улучшить гибкость архитектуры. Концепция универсальная и относится не только к ООП, но и к ФП (даже если называется там по другому) и даже проектированию сервисов

Погодите. Вот допустим у вас есть функция foo, которая вызывает функцию
bar. Вы можете сделать это напрямую или передавать bar как аргумент.
Тогда вы можете сделать foo1 = partial(foo, bar) - и вот у вас внедрение
зависимости в функциональном стиле.

Нет это не внедрение зависимости в ФП стиле, это костыль.
У меня может быть функция moveMoney, которая выполняет сначала валидацию, списывает деньги с одного счета и записывает на другой. Так вот ее определение будет, если упростить:
moveMoney: TransferOperation -> bool = validate >> take >> move

В ФП на нижнем уровне я определяю маленькие функции, кирпичики, а на более высоком я их склеиваю с помощью композиции. Все прекрасно тестируется и может мокатся на более высоких уровнях для интеграционных тестов.

На мой вопрос вы так и не ответили: чем DI мешает или не помогает?

DI просто не нужен, это анахронизм, избыточность. А все что избыточно есть не мера, а антимера.

Концепция универсальная и относится не только к ООП, но и к ФП (даже если называется там по другому)

Если так рефлексировать то можно поставить знак равенства между ООП и ФП, все же ведь программирование в конце концов.

Вот вы делаете маленькие функции, но некоторые из них должны вызывать другие. Вы можете либо передавать их как параметр, либо жестко указывать в коде. В первом случае у нас работает принцип инверсии зависимости (функция не знает что она вызовет, но знает абстракцию), во втором случае у нас невозможно повторное использование функции в чуть других условиях.

Допустим, вы хотите сортировать список по ключи. Либо вы делаете конкретную функцию сортировки по каждому ключу, либо передаете (инжектируете) функцию ключа как параметр.

Вопрос, это все ещё костыль и анахронизм?

В первом случае у нас работает принцип инверсии зависимости (функция не знает что она вызовет, но знает абстракцию), во втором случае у нас невозможно повторное использование функции в чуть других условиях.

Секундочку, мы говорим о концепциях ФП в Питоне. Таких концепциях как функциональная композиция, которые являются частью ФП с самого начала и, следовательно, были реализованны например в Lisp аж в 1958г. Принципы DI увидели свет гораздо позже ООП, как средство закрытия бреши самой концепции ООП. Пожалуйста, вот не нужно, принципы инверсии совать туда где их нет и быть не может.

По сортировке, есть функция sort, которая склеена(не вызывает, а именно склеена!) из более мелких функций. Возьмем пример из Haskell:

sort                    :: (Ord a) => [a] -> [a]
sort                    =  sortBy compare

sortBy                  :: (a -> a -> Ordering) -> [a] -> [a]
sortBy cmp              =  foldr (insertBy cmp) []

insertBy                :: (a -> a -> Ordering) -> a -> [a] -> [a]
insertBy cmp x []       =  [x]
insertBy cmp x ys@(y:ys')
                        =  case cmp x y of
                                GT -> y : insertBy cmp x ys'
                                _  -> x : ys

Посмотрите на сигнатуру: (a -> a -> Ordering) -> [a] -> [a], функция sortBy, это каррированя(по нормальному все функции должны быть только такими) функция, которая "разложена" и при получении первого аргумента, функции (a -> a -> Ordering), вернет нам функцию [a] -> [a], которая уже при получении [a], "сработает" и вернет результат. И может показаться что foldr вызывается и возвращает результат, но это не так, foldr здесь встроена и является частью цепочки. В Питоне можно почти так же писать с помощью toolz например.

Спасибо большое, но я не знаю синтаксис Haskel, мы в треде по питону. Попробую разобраться, поправляйте если я ошибся.

Давайте вернемся к определениям. Что такое композиция функция F(x) и G(x)? Это функция вида F(G(x)). Есть ли у нас такое выше? Нет.

Тут нет композиции, тут есть передача функции в функцию и частичное применение для уменьшения количества аргументов. У нас етсь функция sort_by(list, cmp) и мы её преобразовали в функцию sort(list). Да, в функциональном программировании это есть изначально и есть своя терминология для этого. Но можно так же сказать, что функция sort_by зависит от cmp. Соответсвенно, когда мы её каррируем, мы внедряем эту зависимость.

Но можно так же сказать, что функция sort_by зависит от cmp.

В плане ООП, нельзя, ведь функция не является экземпляром чего-либо(хотя технически это неверно). Рассматривайте ее как часть статического класса на какой-нибудь Java. cmp это аргумент функции только и всего.

Есть ли у нас такое выше? Нет.

Вот как можно представить sortBy если бы у нас был foldrDefEmpty : sortBy = (foldDefEmpty . insertBy), где F это foldDefEmpty, a insertBy G. Что в результате даст foldDefEmpty(insertBy(cmp))

Мы немного в сторону ушли, цель моего комментария сказать что на Питоне можно писать качественный, тестируемый код прибегая к принципам ФП, которые в свою очередь, по полноте своей, обнуляют хоть какой-то смысл использовать DI. Значит ли это что ФП есть единственный путь - нет. Значит ли это что ФП есть самый удобный путь, ИМХО - да (не буду лукавить на Питоне не всегда). Значит ли это что ФП пропитан принципами DI, нет, я бы сказал наоборот, как и все хорошее что можно найти в ООП.

Подскажите плиз, а как в opiod с кастомными скоупами? Допустим я делаю небольшой планировщик (или веб фреймворк или ещё что) и хочу чтобы на каждое событие у меня создавался один новый экземпляр соединения с БД. То есть это привязано не к тредам, а имеет конкретный огранчиенный жизненный цикл, которым будет управлять фреймворк. Можно это реалзиовать?

Sign up to leave a comment.