Подтвердить что ты не робот

Почему IoC/DI не распространен в Python?

В Java IoC/DI является очень распространенная практика, широко используемая в веб-приложениях, почти во всех доступных средах и Java EE. С другой стороны, есть также множество больших веб-приложений Python, но помимо Zope (который, как я слышал, должен быть ужасен для кода), IoC не очень распространен в мире Python. (Пожалуйста, назовите несколько примеров, если вы считаете, что я ошибаюсь).

Конечно, для Python доступны несколько клонов популярных инфраструктур Java IoC, springpython. Но никто из них, похоже, практически не используется. По крайней мере, я никогда не замалчивал Django или sqlalchemy + <insert your favorite wsgi toolkit here>, использующее что-то подобное.

По моему мнению, IoC имеет разумные преимущества и упростит замену django-default-user-model, но широкое использование классов интерфейса и IoC в Python выглядит немного странно, а не "pythonic". Но, возможно, у кого-то есть лучшее объяснение, почему IoC широко не используется в Python.

4b9b3361

Ответ 1

На самом деле я не думаю, что DI/IoC являются необычными в Python. Однако необычными являются каркасы/контейнеры DI/IoC.

Подумайте об этом: что делает контейнер DI? Это позволяет вам

  • объединить независимые компоненты в полное приложение...
  • ... во время выполнения.

У нас есть имена для "соединения вместе" и "во время выполнения":

  • сценарии
  • динамический

Итак, контейнер DI - это не что иное, как интерпретатор для динамического языка сценариев. На самом деле, позвольте мне перефразировать это: типичный контейнер Java/.NET DI - не что иное, как дрянной интерпретатор для очень плохого языка динамических скриптов с упрямым, иногда синтаксисом на основе XML.

Когда вы программируете на Python, почему вы хотите использовать уродливый, плохой язык сценариев, когда у вас есть красивый, блестящий язык сценариев в вашем распоряжении? На самом деле, это более общий вопрос: когда вы программируете практически на любом языке, почему вы хотите использовать уродливый, плохой язык сценариев, когда у вас есть Jython и IronPython в вашем распоряжении?

Итак, чтобы повторить: практика DI/IoC столь же важна в Python, как и в Java, по тем же причинам. Однако реализация DI/IoC встроена в язык и часто настолько легка, что полностью исчезает.

(Здесь краткое изложение для аналогии: в сборке вызов подпрограммы - довольно серьезная сделка - вам нужно сохранить локальные переменные и регистры в память, сохранить где-нибудь адрес возврата, изменить указатель инструкции на подпрограмму вызывают, устраивают его, чтобы каким-то образом вернуться в вашу подпрограмму, когда она закончена, поставить аргументы где-нибудь, где вызывающий может их найти, и т.д. IOW: в сборке "вызов подпрограммы" - это шаблон проектирования, были языки, такие как Fortran, в которые были встроены вызовы подпрограмм, люди строили свои собственные "подпрограммы". Скажете ли вы, что вызовы подпрограмм "необычны" в Python, просто потому, что вы не используете подпрограммы?)

Кстати: для примера того, как это похоже на то, чтобы сделать логический вывод DI, взгляните на Gilad Bracha "Язык программирования новостей" и его статьи по теме:

Ответ 2

Django отлично использует инверсию управления. Например, сервер базы данных выбирается конфигурационным файлом, затем структура предоставляет соответствующие экземпляры оболочки базы данных клиентам базы данных.

Различие заключается в том, что Python имеет первоклассные типы. Типы данных, включая классы, сами являются объектами. Если вы хотите что-то использовать для определенного класса, просто назовите класс. Например:

if config_dbms_name == 'postgresql':
    import psycopg
    self.database_interface = psycopg
elif config_dbms_name == 'mysql':
    ...

Затем более поздний код может создать интерфейс базы данных, написав:

my_db_connection = self.database_interface()
# Do stuff with database.

Вместо функций шаблона factory, которые нужны Java и С++, Python делает это с одной или двумя строками обычного кода. Это сила функционального и императивного программирования.

Ответ 3

Часть этого метода работает в системе Python. Вы можете получить своего рода "singleton" бесплатно, просто импортировав его из модуля. Определите фактический экземпляр объекта в модуле, а затем любой клиентский код может импортировать его и фактически получить рабочий, полностью сконструированный/заполненный объект.

Это отличается от Java, где вы не импортируете фактические экземпляры объектов. Это означает, что вам всегда нужно создавать их самостоятельно (или использовать какой-то подход стиля IoC/DI). Вы можете смягчить все трудности, связанные с необходимостью создавать все сами, имея статические методы factory (или фактические классы factory), но затем вы по-прежнему несете ресурсные издержки, фактически создавая новые каждый раз.

Ответ 4

Не использовал Python через несколько лет, но я бы сказал, что он больше связан с тем, что он является динамически типизированным языком, чем что-либо еще. Для простого примера, на Java, если бы я хотел проверить, что что-то написано стандартным образом, я мог бы использовать DI и передать в любой PrintStream, чтобы захватить написанный текст и проверить его. Однако, когда я работаю в Ruby, я могу динамически заменить метод puts на STDOUT, чтобы выполнить проверку, оставив DI полностью вне изображения. Если единственная причина, по которой я создаю абстракцию, - проверить класс, который его использует (подумайте об операциях файловой системы или о часах в Java), то DI/IoC создает ненужную сложность в решении.

Ответ 5

Я возвращаю ответ "Jörg W Mittag": "Реализация DI/IoC на Python настолько легка, что полностью исчезает".

Чтобы создать резервную копию этого утверждения, взгляните на знаменитый пример Мартина Фаулера, перенесенный с Java на Python: Python:Design_Patterns:Inversion_of_Control

Как видно из приведенной выше ссылки, "Контейнер" в Python может быть записан в 8 строках кода:

class Container:
    def __init__(self, system_data):
        for component_name, component_class, component_args in system_data:
            if type(component_class) == types.ClassType:
                args = [self.__dict__[arg] for arg in component_args]
                self.__dict__[component_name] = component_class(*args)
            else:
                self.__dict__[component_name] = component_class

Ответ 6

IoC/DI - концепция дизайна, но, к сожалению, она часто используется как концепция, применимая к определенным языкам (или системам ввода). Я бы хотел, чтобы контейнеры для инъекций зависимостей стали намного более популярными в Python. Там Spring, но это супер-каркас и, кажется, прямой порт понятий Java без особого внимания к "Путь Python".

Учитывая аннотации в Python 3, я решил получить трещину в полнофункциональном, но простом контейнере для инъекций зависимостей: https://github.com/zsims/dic. Он основан на некоторых концепциях из контейнера инъекций зависимостей .NET(который ИМО является фантастическим, если вы когда-либо играете в этом пространстве), но мутировал с концепциями Python.

Ответ 7

На самом деле, довольно легко написать достаточно чистый и компактный код с DI (интересно, будет ли оно/оставаться pythonic тогда, но в любом случае:)), например, я действительно пересматриваю этот способ кодирования:

def polite(name_str):
    return "dear " + name_str

def rude(name_str):
    return name_str + ", you, moron"

def greet(name_str, call=polite):
    print "Hello, " + call(name_str) + "!"

_

>>greet("Peter")
Hello, dear Peter!
>>greet("Jack", rude)
Hello, Jack, you, moron!

Да, это можно рассматривать как просто простую форму параметризации функций/классов, но она выполняет свою работу. Так что, возможно, и батарей Python, включенных по умолчанию, тоже достаточно.

P.S. Я также опубликовал более крупный пример этого наивного подхода в Динамически оценивая простую логическую логику в Python.

Ответ 8

Я думаю, что из-за динамического характера людей-питонов нередко видна необходимость в другой динамической структуре. Когда класс наследуется от нового объекта "объект", вы можете динамически создавать новую переменную (https://wiki.python.org/moin/NewClassVsClassicClass).

то есть. В обычном python:

#application.py
class Application(object):
    def __init__(self):
        pass

#main.py
Application.postgres_connection = PostgresConnection()

#other.py
postgres_connection = Application.postgres_connection
db_data = postgres_connection.fetchone()

Однако посмотрите https://github.com/noodleflake/pyioc, это может быть то, что вы ищете.

i.e. В pyioc

from libs.service_locator import ServiceLocator

#main.py
ServiceLocator.register(PostgresConnection)

#other.py
postgres_connection = ServiceLocator.resolve(PostgresConnection)
db_data = postgres_connection.fetchone()

Ответ 9

По-моему, такие вещи, как инъекция зависимостей, являются симптомами жесткой и сложной структуры. Когда основной код кода становится слишком весомым, чтобы легко меняться, вы обнаружите, что вам нужно выбирать его мелкие части, определять интерфейсы для них, а затем позволять людям изменять поведение через объекты, подключаемые к этим интерфейсам. Это все хорошо и хорошо, но лучше избегать такой сложности в первую очередь.

Это также симптом статически типизированного языка. Когда единственным инструментом, который вы должны выразить абстракцией, является наследование, то это почти то, что вы используете повсюду. Сказав это, С++ довольно похож, но никогда не подхватил увлечение строителями и интерфейсами во всем, что делали разработчики Java. Легко переоценить мечту быть гибкой и расширяемой ценой написания слишком большого общего кода с небольшой реальной выгодой, Я думаю, что это культурная вещь.

Обычно я думаю, что люди Python используются для выбора правильного инструмента для задания, который является связным и простым целым, а не One True Tool (с помощью Thousand Возможные плагины), который может делать что угодно, но предлагает недоумение возможные перестановки конфигурации. При необходимости все еще есть взаимозаменяемые части, но при этом не требуется большой формализм определения фиксированных интерфейсов из-за гибкости утиного ввода и относительной простоты языка.

Ответ 10

Я согласен с @Jorg в том, что DI/IoC возможно, проще и даже красивее в Python. Что не хватает в поддерживающих его инфраструктурах, но есть несколько исключений. Чтобы указать несколько примеров, которые приходят мне на ум:

  • Комментарии Django позволяют проводить собственный класс комментариев с помощью вашей логики и форм. [Дополнительная информация]

  • Django позволяет использовать пользовательский объект профиля для присоединения к вашей модели пользователя. Это не полностью IoC, но это хороший подход. Лично я хотел бы заменить модель User в качестве рамки комментариев. [Дополнительная информация]

Ответ 11

В отличие от сильной типизированной природы в Java. Поведение при наборе утинов Python позволяет легко передавать объекты.

Разработчики Java сосредоточены на построении класса strcuture и отношениях между объектами, сохраняя при этом все гибкость. IoC чрезвычайно важна для достижения этого.

Разработчики Python сосредоточены на выполнении работы. Они просто подключают классы, когда им это нужно. Им даже не нужно беспокоиться о типе класса. Пока это может крякать, это утка! Эта природа не оставляет места для IoC.