В Java IoC / DI является очень распространенной практикой, которая широко используется в веб-приложениях, почти во всех доступных средах и Java EE. С другой стороны, есть также много больших веб-приложений на Python, но помимо Zope (который, как я слышал, должно быть ужасно кодировать), IoC, похоже, не очень распространен в мире Python. (Пожалуйста, назовите несколько примеров, если вы считаете, что я ошибаюсь).
Конечно, есть несколько клонов популярных фреймворков Java IoC, доступных для Python, например , Springpython . Но никто из них, кажется, не привык практически. По крайней мере, я никогда не сталкивался с веб-приложением на основе Django или sqlalchemy +, <insert your favorite wsgi toolkit here>
которое использует что-то подобное.
На мой взгляд, IoC имеет разумные преимущества и позволит легко заменить django-default-user-model, например, но широкое использование классов интерфейса и IoC в Python выглядит немного странно, а не «pythonic». Но, возможно, у кого-то есть лучшее объяснение, почему IoC не широко используется в Python.
Ответы:
Я на самом деле не думаю , что DI / IoC являются , что редкость в Python. Что является необычным, однако, DI / IoC каркасы / контейнеры .
Подумайте об этом: что делает контейнер DI? Это позволяет вам
У нас есть имена для «соединения вместе» и «во время выполнения»:
Таким образом, контейнер DI - это не что иное, как интерпретатор динамического языка сценариев. На самом деле, позвольте мне перефразировать это: типичный контейнер Java / .NET DI - не что иное, как дурацкий интерпретатор для действительно плохого языка динамических сценариев с непривлекательным, иногда основанным на XML, синтаксисом.
Когда вы программируете на Python, зачем вам использовать уродливый, плохой язык сценариев, когда в вашем распоряжении прекрасный, блестящий язык сценариев? На самом деле, это более общий вопрос: когда вы программируете практически на любом языке, зачем вам использовать уродливый, плохой язык сценариев, когда у вас есть Jython и IronPython?
Итак, подведем итог: практика использования DI / IoC в Python так же важна, как и в Java, по тем же причинам. реализация DI / IoC встроена в язык и часто настолько легка, что полностью исчезает.
(Вот краткое замечание для аналогии: в ассемблере вызов подпрограммы является довольно серьезной проблемой - вы должны сохранить свои локальные переменные и регистры в памяти, где-то сохранить свой обратный адрес, изменить указатель инструкции на вызываемую подпрограмму, сделайте так, чтобы он каким-то образом возвращался в вашу подпрограмму после ее завершения, помещал аргументы куда-то, где вызываемый может их найти, и т. д. IOW: в сборке «вызов подпрограммы» является шаблоном проектирования, и до того, как появились такие языки, как Fortran, в котором были встроены вызовы подпрограмм, люди строили свои собственные "структуры подпрограмм". Вы бы сказали, что вызовы подпрограмм "необычны" в Python, просто потому, что вы не используете структуры подпрограмм?)
BTW: для примера того , что он выглядит как взять DI к своему логическому завершению, взгляните на Гилада Браха «s новояза Язык программирования и его труды по этой теме:
источник
Отчасти это то, как система модулей работает в Python. Вы можете получить своего рода «синглтон» бесплатно, просто импортировав его из модуля. Определите фактический экземпляр объекта в модуле, и тогда любой клиентский код может импортировать его и фактически получить работающий, полностью построенный / заполненный объект.
Это в отличие от Java, где вы не импортируете фактические экземпляры объектов. Это означает, что вам всегда приходится создавать их экземпляры самостоятельно (или использовать какой-то подход стиля IoC / DI). Вы можете уменьшить необходимость создавать все самостоятельно, используя статические фабричные методы (или фактические фабричные классы), но тогда вы все равно будете нести ресурсы, фактически создавая новые каждый раз.
источник
MyClassInstances
класс для каждогоMyClass
в Java, который содержит только статические, полностью инициализированные экземпляры. Это будет проводной: Dfrom framework.auth.user import User
этого может быть лучше написатьUser = lookup('UserImplentation', 'framework.auth.user.User')
(2-й параметр может быть значением по умолчанию) внутри фреймворка. Тогда пользователи фреймворка смогут заменить / специализироватьUser
реализацию, не касаясь фреймворка.IoC и DI очень распространены в зрелом коде Python. Вам просто не нужен каркас для реализации DI, благодаря утилитному типу.
Лучший пример - как настроить приложение Django, используя
settings.py
:Django Rest Framework активно использует DI:
Напомню ( источник ):
источник
IsAuthenticated
,ScopedRateThrottle
) создаются классом. Они не передаются в конструктор.IsAuthenticated
иScopedRateThrottle
не экземпляры, это классы. Они создаются при создании FooView (фактически, когда FooView обрабатывает запрос). В любом случае, это просто деталь реализации.IsAuthenticated
иScopedRateThrottle
являются зависимостями; они вводятся вFooView
. Неважно, когда и как это делается. Python не является Java, поэтому есть разные способы реализовать это.renderer_classes = (JSONRenderer, BrowsableAPIRenderer, XMLRenderer)
. Дразнить так же просто, как@unittest.patch('myapp.views.FooView.permission_classes')
. Отчаянная необходимость «передавать что-то» является следствием «способа выполнения действий на Java», поскольку Java является скомпилированным и статически типизированным языком, в котором отсутствуют сильные возможности метапрограммирования.Джанго отлично использует инверсию контроля. Например, сервер базы данных выбирается файлом конфигурации, а затем инфраструктура предоставляет соответствующие экземпляры оболочки базы данных клиентам базы данных.
Разница в том, что Python имеет первоклассные типы. Типы данных, включая классы, сами являются объектами. Если вы хотите, чтобы что-то использовало определенный класс, просто назовите класс. Например:
Более поздний код может затем создать интерфейс базы данных, написав:
Вместо стандартных фабричных функций, которые нужны Java и C ++, Python делает это с помощью одной или двух строк обычного кода. В этом сила функционального и императивного программирования.
источник
import psycopg2 as database_interface
. Поместите эту строку вinjections.py
et voilà.self.database_interface
), которые крайне необходимы.Он видит, что люди больше не понимают, что означает внедрение зависимостей и инверсия управления.
Практика использования инверсии управления состоит в том, чтобы иметь классы или функции, которые зависят от других классов или функций, но вместо создания экземпляров, в которых находится класс функционального кода, лучше получить его в качестве параметра, поэтому слабая связь может быть заархивирована. Это имеет много преимуществ, таких как большая тестируемость и возможность замещения принципа замены Лискова.
Вы видите, что при работе с интерфейсами и инъекциями ваш код становится более удобным, так как вы можете легко изменить его поведение, потому что вам не придется переписывать одну строку кода (возможно, одну или две строки в конфигурации DI) вашего класс, чтобы изменить его поведение, так как классы, которые реализуют интерфейс, который ожидает ваш класс, могут изменяться независимо, пока они следуют за интерфейсом. Одна из лучших стратегий для того, чтобы код был развязан и прост в обслуживании, - это следовать по крайней мере единым принципам ответственности, замены и инверсии зависимостей.
Для чего нужна библиотека DI, если вы можете создать экземпляр объекта внутри пакета и импортировать его, чтобы внедрить его самостоятельно? Выбранный ответ правильный, так как в Java нет процедурных разделов (код вне классов), все, что входит в скучные XML-файлы конфигурации, и, следовательно, необходимость класса для создания и вставки зависимостей в режиме ленивой загрузки, чтобы вы не сдували ваша производительность, в то время как на python вы просто кодируете инъекции в «процедурные» (код вне классов) вашего кода
источник
Python не использовался несколько лет, но я бы сказал, что он больше связан с тем, что он является языком с динамической типизацией, чем с чем-либо еще. Для простого примера, в Java, если бы я хотел проверить, что-то записано в соответствии со стандартом, я мог бы использовать DI и передать любой PrintStream, чтобы захватить записываемый текст и проверить его. Тем не менее, когда я работаю в Ruby, я могу динамически заменять метод «put» на STDOUT для проверки, оставляя DI полностью вне поля зрения. Если единственной причиной, по которой я создаю абстракцию, является тестирование класса, который ее использует (например, операции файловой системы или часы в Java), то DI / IoC создает ненужную сложность в решении.
источник
На самом деле, довольно просто написать достаточно чистый и компактный код с помощью DI (интересно, будет ли он / останется тогда пифоническим , но в любом случае :)), например, я на самом деле предпочитаю такой способ кодирования:
_
Да, это можно рассматривать как простую форму параметризации функций / классов, но это делает свою работу. Так что, может быть, батарейки Python, включенные по умолчанию, здесь тоже достаточно.
PS Я также опубликовал более крупный пример этого наивного подхода в разделе Динамическая оценка простой логической логики в Python .
источник
IoC / DI - это концепция дизайна, но, к сожалению, ее часто принимают за концепцию, которая применяется к определенным языкам (или системам ввода). Я бы хотел, чтобы контейнеры для инъекций зависимостей стали намного популярнее в Python. Есть Spring, но это супер-фреймворк, и он, кажется, является прямым портом концепций Java без особого рассмотрения «Пути Python».
Учитывая аннотации в Python 3, я решил взломать полнофункциональный, но простой контейнер внедрения зависимостей: https://github.com/zsims/dic . Он основан на некоторых концепциях из контейнера для внедрения зависимостей .NET (что является ИМО фантастическим, если вы когда-либо играете в этом пространстве), но мутирует с концепциями Python.
источник
Я думаю, что из-за динамической природы Python люди не часто видят необходимость в другой динамической среде. Когда класс наследует от «объекта» нового стиля, вы можете динамически создать новую переменную ( https://wiki.python.org/moin/NewClassVsClassicClass ).
т.е. в простом питоне:
Однако взгляните на https://github.com/noodleflake/pyioc это может быть то, что вы ищете.
то есть в пиок
источник
other.py
строке 1 есть автоматическое разрешение зависимостей, но оно не будет учитываться как внедрение зависимостей.Я возвращаюсь к ответу «Йорг Миттаг»: «Реализация DI / IoC на Python настолько легка, что полностью исчезает».
Чтобы поддержать это утверждение, взгляните на знаменитый пример Мартина Фаулера, портированный с Java на Python: Python: Design_Patterns: Inversion_of_Control
Как видно из приведенной выше ссылки, «Контейнер» в Python может быть записан в 8 строках кода:
источник
Мой 2cents заключается в том, что в большинстве приложений Python он вам не нужен, и даже если вам это нужно, есть вероятность, что многие ненавистники Java (и некомпетентные фиддлеры, которые считают себя разработчиками) считают это чем-то плохим, просто потому, что он популярен в Java ,
Система IoC на самом деле полезна, когда у вас есть сложные сети объектов, где каждый объект может зависеть от нескольких других и, в свою очередь, сам быть зависимым от других объектов. В таком случае вы захотите определить все эти объекты один раз и иметь механизм для автоматического их объединения, основанный на максимально возможном количестве неявных правил. Если у вас также есть конфигурация, которая должна быть определена простым способом пользователем / администратором приложения, это еще одна причина, чтобы захотеть систему IoC, которая может считывать свои компоненты из чего-то вроде простого XML-файла (который будет конфигурацией).
Типичное приложение Python намного проще, просто набор сценариев, без такой сложной архитектуры. Лично я знаю, что такое IoC (в отличие от тех, кто написал определенные ответы здесь), и я никогда не чувствовал необходимости в этом из-за моего ограниченного опыта работы с Python (также я не использую Spring везде, не когда преимущества это не оправдывает его развитие).
Тем не менее, существуют ситуации Python, где подход IoC действительно полезен, и, на самом деле, я читал здесь, что Django использует его.
Те же рассуждения, приведенные выше, могут быть применены к аспектно-ориентированному программированию в мире Java, с той разницей, что число случаев, когда AOP действительно стоит, еще более ограничено.
источник
The typical Python application is much simpler, just a bunch of scripts, without such a complex architecture.
- довольно предположениесветильники Pytest основаны на DI ( источник )
источник
Я согласен с @Jorg в том, что DI / IoC возможен, проще и еще красивее в Python. Чего не хватает, так это фреймворков, поддерживающих его, но есть несколько исключений. Чтобы привести пару примеров, которые приходят мне в голову:
Комментарии Django позволяют вам связать свой собственный класс Comment с вашей собственной логикой и формами. [Больше информации]
Django позволяет использовать пользовательский объект профиля для присоединения к вашей модели пользователя. Это не совсем IoC, но это хороший подход. Лично я хотел бы заменить дырочную модель пользователя, как это делает структура комментариев. [Больше информации]
источник
По моему мнению, такие вещи, как внедрение зависимости, являются симптомами жесткой и слишком сложной структуры. Когда основная часть кода становится слишком тяжелой, чтобы ее можно было легко изменить, вы обнаруживаете, что должны выбрать небольшие ее части, определить интерфейсы для них, а затем позволить людям изменять поведение с помощью объектов, подключаемых к этим интерфейсам. Это все хорошо, но в первую очередь лучше избегать подобных сложностей.
Это также признак статически типизированного языка. Когда единственным средством выражения абстракции является наследование, это то, что вы используете везде. Сказав это, C ++ очень похож, но никогда не привлекал внимание везде, где это делали разработчики Java, с Builders и Interfaces. Легко стать чрезмерно обильным с мечтой быть гибким и расширяемым за счет написания слишком большого количества общего кода с небольшой реальной выгодой . Я думаю, что это культурная вещь.
Обычно я думаю, что Python люди привыкли выбирать правильный инструмент для работы, который представляет собой единое и простое целое, а не One True Tool (с тысячей возможных плагинов), который может делать все что угодно, но предлагает изумительный массив возможных перестановок конфигурации , Там по-прежнему есть взаимозаменяемые части, где это необходимо, но без необходимости большого формализма определения фиксированных интерфейсов, из-за гибкости типизации утилит и относительной простоты языка.
источник
EmailSender
и я решу заменить их на aDesktopNotifier
, я должен отредактировать 12 классов вручную. И вы думаете, что это проще и чище, чем просто писать вINotifier
интерфейс и позволить контейнеру проработать детали?В отличие от строго типизированной природы в Java. Поведение Python по типу "утка" позволяет легко передавать объекты.
Java-разработчики сосредоточены на создании класса strcuture и отношений между объектами, сохраняя при этом гибкость. IoC чрезвычайно важен для достижения этой цели.
Разработчики Python сосредоточены на выполнении работы. Они просто подключают классы, когда им это нужно. Им даже не нужно беспокоиться о типе класса. Пока это может крякать, это утка! Эта природа не оставляет места для IoC.
источник