Почему IoC / DI не распространены в Python? - PullRequest
272 голосов
/ 17 марта 2010

В 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.

Ответы [ 14 ]

177 голосов
/ 17 марта 2010

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

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

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

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

  1. скриптовый
  2. динамический * * один тысяча двадцать две

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

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

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

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

Кстати: для примера того, как выглядит вывод DI к его логическому завершению, взгляните на Gilad Bracha Язык программирования Newspeak и его сочинения на эту тему :

45 голосов
/ 17 марта 2010

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

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

36 голосов
/ 17 марта 2010

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

Разница в том, что 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.

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

22 голосов
/ 30 июня 2018

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

Лучший пример - как настроить приложение Django с помощью settings.py:

# settings.py
CACHES = {
    'default': {
        'BACKEND': 'django_redis.cache.RedisCache',
        'LOCATION': REDIS_URL + '/1',
    },
    'local': {
        'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
        'LOCATION': 'snowflake',
    }
}

Django Rest Framework активно использует DI:

class FooView(APIView):
    # The "injected" dependencies:
    permission_classes = (IsAuthenticated, )
    throttle_classes = (ScopedRateThrottle, )
    parser_classes = (parsers.FormParser, parsers.JSONParser, parsers.MultiPartParser)
    renderer_classes = (renderers.JSONRenderer,)

    def get(self, request, *args, **kwargs):
        pass

    def post(self, request, *args, **kwargs):
        pass

Позвольте напомнить ( источник ):

«Инъекция зависимости» - это термин на 25 долларов для концепции с 5 центами. [...] Внедрение зависимости означает предоставление объекту его переменных экземпляра. [...].

12 голосов
/ 17 марта 2010

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

9 голосов
/ 09 мая 2018

Видно, что люди больше не понимают, что означает внедрение зависимостей и инверсия управления.

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

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

Для чего нужна библиотека DI, если вы можете создать экземпляр объекта внутри пакета самостоятельно?и импортировать это, чтобы ввести это самостоятельно?Выбранный ответ правильный, так как в Java нет процедурных разделов (код вне классов), все, что входит в скучные XML-файлы конфигурации, следовательно, необходимость класса для создания и внедрения зависимостей в режиме ленивой загрузки, чтобы вы не сдуваливаша производительность, в то время как на python вы просто кодируете инъекции в «процедурные» (код вне классов) вашего кода

9 голосов
/ 24 декабря 2014

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

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

7 голосов
/ 18 марта 2010

На самом деле, довольно просто написать достаточно чистый и компактный код с помощью 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 .

5 голосов
/ 05 сентября 2011

Я возвращаюсь к ответу «Йорг Миттаг»: «Реализация 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
4 голосов
/ 28 сентября 2015

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

т В простом питоне:

#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 это может быть то, что вы ищете.

то есть In pyioc

from libs.service_locator import ServiceLocator

#main.py
ServiceLocator.register(PostgresConnection)

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