Чому IoC / DI не часто зустрічається в Python?


312

У 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-user, але широке використання класів інтерфейсів та IoC в Python виглядає дещо дивним і не «пітонічним». Але, можливо, хтось має краще пояснення, чому IoC не використовується широко в Python.


2
Я здогадуюсь, та сама причина, що вона менш популярна в Ruby, вбудованих міксинах та відкритих класах
Сем Сафрон

3
ви коли-небудь пробували springpython? він навіть не працює, як рекламується. щонайменше, в частині Aop. все інше там не дуже корисно, якщо ви не приїжджаєте з Java та не потребуєте певного рівня комфорту під час переходу.
Том Вілліс

6
Будь ласка, подбайте про те, щоб розрізняти використання DI та використання рамки МОК. Перший - це модель дизайну, другий - це рамка для автоматизованого використання першого.
Дуг

Дуг, я вважаю, ти мав на увазі сказати, що DI - це креативна особливість, яка отримується за допомогою шаблону декоратора.
njappboy

4
Я хотів би побачити відповідь, яка стосується реальних світових проблем, які вирішує DI: управління життям, простота тестування та ін. Якщо є більш пітонічний спосіб вирішити ці проблеми, я все чую.
Джош Ное

Відповіді:


197

Я насправді не думаю, що 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, тільки тому, що ви не використовуєте рамки підпрограми?)

BTW: для прикладу того, як виглядає сприйняття DI до його логічного завершення, подивіться на мову новомовного програмування Гілада Брача та його статті на цю тему:


58
Поки я згоден. Коментар XML неправильний. Багато (принаймні сучасні) контейнери МОК використовують конвенцію (код) над конфігурацією (XML).
Фінглас

20
Ніщо не заважає вам чітко писати проводку на Java, але оскільки у вас є все більше служб, залежність стає складнішою. Контейнер DI схожий на Make: ви оголошуєте залежності, і контейнер ініціалізує їх у правильному порядку. Guice - це рамка Java DI, де все написано в коді Java. Написавши декларативно, контейнер DI також додає підтримку після обробки обробки декларацій перед ініціалізацією (наприклад, замінити заповнювачі властивостей фактичними значеннями)
IttayD

133
"Однак реалізація DI / IoC вбудована в мову і часто настільки легка, що повністю зникає". Проголосуйте проти, оскільки це категорично не відповідає дійсності. DI - це шаблон, коли інтерфейс передається конструктору. Він не вбудований у пітон.
Дуг

146
downvote, проводка разом не має нічого спільного зі сценарієм, DI - це зразок, і це не рівноцінно сценаріям
Luxspes

38
Я не згоден з цим. DI не вирішує відсутність динамічного сценарію в статичних мовах. Він надає основу для налаштування та складання частин програми. Одного разу я чув, як Рубі розробити, що в динамічних мовах DI не потрібен. Але він використовував Rails ... Rails - це просто великий тип контейнерів DI, який використовує конвенцію, щоб визначити, які частини потрібно налаштувати, коли. Йому не потрібен був DI, тому що Rails вирішив проблему пошуку деталей для нього.
Брайан Генісіо

51

Частина його - це те, як працює модульна система в Python. Ви можете отримати свого роду "одиночний" безкоштовно, просто імпортувавши його з модуля. Визначте фактичний екземпляр об'єкта в модулі, і тоді будь-який код клієнта може імпортувати його і фактично отримати робочий, повністю побудований / заселений об'єкт.

Це на відміну від Java, куди ви не імпортуєте фактичні екземпляри об'єктів. Це означає, що вам завжди доведеться інстанціювати їх самостійно (або використовувати якийсь підхід у стилі IoC / DI). Ви можете пом'якшити клопоти з необхідності придумати все самостійно, встановивши статичні фабричні методи (або фактичні фабричні класи), але тоді ви все одно несете ресурси, що фактично створюють нові.


2
Що має сенс. Якщо я хочу змінити реалізацію в Python, я просто імпортую з іншого місця, використовуючи те саме ім’я. Але зараз я думаю, чи можна також навпаки, визначивши MyClassInstancesклас для кожного MyClassз Java, який містить лише статичні, повністю ініціалізовані екземпляри. Це було б провідним: D
tux21b

2
І ще одна ідея: надання способу зміни такого імпорту в python дозволить легко замінити реалізацію, не торкаючись усіх файлів python. Замість from framework.auth.user import User цього може бути краще записати User = lookup('UserImplentation', 'framework.auth.user.User')(другий параметр може бути значенням за замовчуванням) всередині рамки. Тоді користувачі фреймворку зможуть замінити / спеціалізувати Userреалізацію, не торкаючись рамки.
tux21b

14
Просте спрощення, відповідь, в реальному житті вам рідко потрібен лише "синглтон", вам потрібно контролювати область (можливо, вам знадобиться локальний потік синглтон, або сеанс синглтон і так далі), це змушує мене думати, що такі проблеми вирішені в Python - це не такі реальні проблеми, які насправді вирішуються в умовах підприємства
Luxspes

3
Насправді DI - це можливість тестування та деакуляції залежностей коду. Також функція імпорту схожа на статичний імпорт на Java, що дозволяє мені імпортувати один екземпляр об'єкта.
Річард Уорбуртон

1
"Ви можете отримати своєрідний" singleton "безкоштовно, просто імпортувавши його з модуля." Можна легко зробити в Java, оголосивши статичне поле екземпляра і встановивши його значення. Це не sol
ggranum

45

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 центів. [...] Введення залежності залежить від надання об'єкту змінних його примірників. [...].


8
+1. Добре кажучи. Будучи програмістом Python, мене повністю вразила ціла презентація інтерв'ю про рамки DI в C #. Мені потрібно було деякий час, щоб зрозуміти, що я вже робив це постійно у програмах Flask, навіть не замислюючись про це, тому що вам не потрібна основа. Для того, хто нічого не знає, крім C # / Java, питання має сенс. Для програмістів на мові набраних качок це цілком природно і, як ви кажете, «25-доларовий термін для 5-відсоткової концепції».
Семюел

5
Помилка ... це не введення залежності, оскільки екземпляри ( IsAuthenticated, ScopedRateThrottle) інстанціюються класом. Вони не передаються в конструктор.
допатраман

5
IsAuthenticatedі ScopedRateThrottleне є екземплярами, це класи. Вони створюються при створенні FooView (фактично, коли FooView обробляє запит). У будь-якому випадку, це лише деталь реалізації. IsAuthenticatedі ScopedRateThrottleє залежностями; вони вводяться всередину FooView. Не має значення, коли і як це робиться. Python - це не Java, тому існують різні способи їх реалізації.
Макс Малиш

3
@MaxMalysh Я погоджуюся з dopatraman на цьому. Це навіть не IoC, оскільки сам клас має «жорсткі» залежності від конкретного класу. В IoC залежність повинна бути надана замість жорсткого коду. На додаток до цього, у системі "Ін'єкція залежностей" ви матимете організацію, відповідальну за управління життєвими циклами кожної служби та вводити їх, якщо це так. Рішення надано не в одному з цих.
Рікардо Алвеш

3
@alex Ні, вам не потрібно змінювати код, щоб використовувати інший рендер. Ви навіть можете використовувати кілька рендерів одночасно: renderer_classes = (JSONRenderer, BrowsableAPIRenderer, XMLRenderer). Знущання так само просто @unittest.patch('myapp.views.FooView.permission_classes'). Відчайдушна потреба "щось передати" є наслідком "способу Java робити речі" через те, що Java є компільованою та статично набраною мовою, що не має сильних можливостей метапрограмування.
Макс Малиш

35

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

Різниця полягає в тому, що 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 виконує це одним або двома рядками звичайного коду. Це сила функціонального та імперативного програмування.


4
Те, що ви називаєте кодом, насправді є частиною проводки. Це був би XML вашої системи ioc. Це насправді може бути написане просто так import psycopg2 as database_interface. Поставте цей рядок у injections.pyet voilà.
спектри

29
Ерм. Те, що ви там робите, - це надзвичайно імперативний підручник Данило.
Shayne

Це безумовно імперативний код, але він є на зразок функціонального, оскільки він використовує дзвінок як значення.
Джеремі

5
Хіба це не лише функції першого класу? en.wikipedia.org/wiki/First-class_function Тільки тому, що ви маєте та використовуєте їх, не робить ваш код функціональним. Тут відбувається чимало побічних ефектів (таких як зміна self.database_interface), які кричать обов'язково.
hjc1710

15

Здається, що люди справді не отримують того, що вводить залежність та інверсія управління.

Практика використання інверсії управління полягає в тому, щоб мати класи або функції, які залежать від інших класів або функцій, але замість того, щоб створювати екземпляри, що входять до класу коду функції, краще отримувати його як параметр, тому може бути досягнуто вільне з'єднання. Це має багато переваг, як більш перевіряти і досягти принципу заміни Ліскова.

Розуміючи, працюючи з інтерфейсами та ін'єкціями, ваш код стає більш керованим, оскільки ви можете легко змінити поведінку, оскільки вам не доведеться переписувати один рядок коду (можливо, рядок чи два в конфігурації DI) вашого клас, щоб змінити свою поведінку, оскільки класи, які реалізують інтерфейс, якого очікує ваш клас, можуть змінюватися незалежно, доки вони слідують за інтерфейсом. Однією з найкращих стратегій, щоб тримати код, нерозв'язаний та простий у обслуговуванні, є дотримання принаймні єдиних принципів відповідальності, заміни та інверсії залежності.

Для чого корисна бібліотека DI, якщо ви можете інстанціювати об’єкт самостійно всередині пакета та імпортувати його, щоб ввести його самостійно? Обрана відповідь правильна, оскільки java не має процедурних розділів (код за межами класів), все, що входить у нудну конфігурацію xml, отже, необхідність класу інстанціювати та вводити залежності від ледачого навантаження, щоб не дути ваша ефективність, тоді як на python ви просто кодуєте ін'єкції в "процедурних" (код поза класами) розділах вашого коду


ви все ще пропускаєте, що IoC / DI автоматично об'єднує об'єкти. Це не так вже й вдається зробити це під час виконання (Java може це зробити за допомогою відображення в будь-якому випадку), це те, що рамка піклується про це, і вам не потрібно робити це прямо. Наявність процедурних розділів також не має значення, ніщо не заважає написати повністю процедурний додаток на Java, використовуючи класи як просто контейнери статичних підпрограм та функцій, без використання функцій OOP взагалі.
zakmck

@zakmck: "процедурний" розділ Python тут насправді не стосується написання процесуального коду. Те, що робить «процедурний» розділ Python відмінним від статичних мов, - це можливість помістити процедурний код у тіло класу, яке працює під час визначення класу, і ввести оператори імпорту всередині if-оператора, а також створити заводський клас класів просто шляхом визначення класів всередині фабричного методу. Це речі, які неможливо зробити статичними мовами, і які вирішують більшість проблем, які намагалися вирішити IOC / DI. Метапрограмування в Python часто виглядає як звичайний код Python.
Лежать Раян

@LieRyan, ви можете це зробити за допомогою рефлексії, або, якщо вам це потрібно часто або під час виконання, ви можете зателефонувати статичній мові з іншої мови, як Groovy (яка призначена для легкої гри з Java) або навіть самого Python. Тим не менш, це має мало спільного з рамками IoC / DI, оскільки їх мета полягає в тому, щоб зробити більшість процедурних об'єктів проводкою для вас автоматично, використовуючи лише визначення. На жаль, більшість відповідей у ​​цій справі пропускають це питання.
zakmck

12

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


3
Мене ніколи не перестає дивувати те, що люди, які бажають змінити, як працює система, перевіряють, що вона працює. Тепер вам потрібно перевірити, що ваші тести не викликають побічних ефектів.
Основні

2
він говорить про зміну методу put лише в тестовій області, це як макет метод введеного об'єкта.
dpa

2
@Basic, що цілком нормально в одиничних тестах , насправді доцільно робити це в цих тестах, оскільки ви не хочете забруднювати покриття тестового випадку більш ніж одним блоком коду (тим, що тестується). Хоча це було б неправильно робити для інтеграційних тестів, можливо, саме так ви посилаєтесь на свій коментар?
samuelgrigolato

1
Для мене заповітність - це питання першокласного. Якщо дизайн не перевіряється, це не гарний дизайн, і я не маю проблем із зміною дизайну, щоб зробити його більш тестовим. Мені доведеться повторно підтвердити, що це все ще працює, але це нормально. Заповітність - цілком вагома причина зміни коду IMO
Карлос Родрігес

10

Насправді, досить просто написати достатньо чистий і компактний код за допомогою DI (мені цікаво, чи буде це / залишатися пітонічним , але все одно :)), наприклад, я фактично переглядаю цей спосіб кодування:

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, за замовчуванням.

PS Я також розмістив більший приклад такого наївного підходу при динамічному оцінюванні простої булевої логіки в Python .


3
Для простих випадків, які можуть працювати, але просто уявіть собі простий контролер веб-блогу, який використовує різні моделі (повідомлення, коментар, користувач). Якщо ви бажаєте, щоб користувач вводив власну модель публікації (з додатковим атрибутом перегляду для відстеження цього) та власну модель користувача з додатковою інформацією про профіль тощо, всі параметри можуть виглядати заплутано. Крім того, користувач може також захотіти змінити об’єкт Запити, щоб підтримати сеанс файлової системи замість простого сеансу на основі файлів cookie чи щось подібне ... Отже, незабаром у вас з’явиться безліч параметрів.
tux21b

1
@ tux21b Ну, є "суттєва складність", яку користувачі хочуть, щоб програма застосувала, є архітектурні рішення для неї (деякі з них не гірші за решту з точки зору розробки та, можливо, часу обслуговування, відповідно швидкості тощо). ), і є здатність людини розуміти API та архітектуру програмного забезпечення. Якщо взагалі немає зрозумілого для людини рішення (не лише серед тих, хто використовує (будь-яку форму) DI) ... ну хто сказав, що всі проблеми вирішуються? І наявність безлічі параметрів, призначених за замовчуванням (але їх можна змінювати за вибором користувача), насправді досить часто.
mlvljr

9

IoC / DI - це концепція дизайну, але, на жаль, її часто сприймають як концепцію, яка застосовується до певних мов (або машинописних систем). Я хотів би бачити, щоб контейнери для ін'єкцій залежностей стали набагато популярнішими в Python. Там весна, але це суперкадр і, здається, є прямим портом концепцій Java без особливого розгляду для "Шлях Python".

З огляду на Анотації в Python 3, я вирішив отримати тріщину у повнофункціональному, але простому контейнері для ін'єкцій залежності: https://github.com/zsims/dic . Він заснований на деяких поняттях з контейнера для ін'єкцій залежності .NET (який IMO є фантастичним, якщо ви коли-небудь граєте в цьому просторі), але мутований з концепціями Python.


6

Я думаю, що через динамічний характер пітона люди часто не бачать потреби в іншому динамічному середовищі. Коли клас успадковує новий об'єкт стилю, ви можете динамічно створювати нову змінну ( 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, це може бути те, що ви шукаєте.

тобто в піоку

from libs.service_locator import ServiceLocator

#main.py
ServiceLocator.register(PostgresConnection)

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

2
Сам факт, що обидві версії мають однакову кількість коду, проходить довгий шлях до пояснення, чому використання фреймворку не дуже популярне.
спектри

У other.pyрядку 1 є автоматичне вирішення залежності, але він не вважає це введенням залежності.
andho

Локатори сервісу, як правило, є анти-шаблоном.
PmanAce

6

Я повертаюсь до відповіді "Йорг W Міттаг": "Реалізація 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

42
Це далеко не навіть у найслабших контейнерів DI. Де управління життям, рекурсивна залежність залежності, здатність висміювати або - не вдається все це - конфігурація? Це не що інше, як пошук типу та кеш-пам'яті, що не те саме, що IoC.
Основні

2
Роки тому я написав невелику рамку DI, використовуючи метакласи як вправу. Вся справа в єдиному файлі з нульовим імпортом і доктестами, які роблять це самоосмисленим. Це показує, що базові особливості не так важко реалізувати таким чином, що є навіть "пітонічним", але я щиро думаю, що сумно, що жодне повне рішення не набуло великої тяги, як у Spring на Java, і всі роблять власні архітектури плагінів.
Андреа Рато

2

Мої 2 центи полягають у тому, що у більшості додатків Python він вам не потрібен, і навіть якщо він вам потрібен, є велика ймовірність, що багато ненависників Java (і некомпетентні скрипачі, які вважають розробниками) вважають це як щось погане, лише тому, що воно популярне на Java .

Система IoC насправді корисна, коли у вас є складні мережі об'єктів, де кожен об'єкт може бути залежністю для кількох інших і, у свою чергу, бути самим залежним від інших об'єктів. У такому випадку ви хочете один раз визначити всі ці об'єкти і мати механізм їх автоматичного з’єднання разом на основі якомога більше неявних правил. Якщо ви також маєте конфігурацію, яку слід просто визначити користувачем / адміністратором програми, це додатковий привід бажати системи IoC, яка може читати її компоненти з чогось на зразок простого XML-файлу (яка би була конфігурація).

Типовий додаток Python набагато простіший, просто купа сценаріїв, без такої складної архітектури. Особисто я знаю, що насправді є IoC (всупереч тим, хто писав тут певні відповіді), і я ніколи не відчував потреби в цьому в моєму обмеженому досвіді Python (також я не використовую Весну скрізь, не коли переваги це дає не виправдовувати його розвиток накладних витрат).

Однак, є ситуації з Python, коли підхід IoC насправді корисний, і, власне, я прочитав тут, що Django використовує його.

Це ж міркування вище можна було б застосувати до орієнтованого на аспекти програмування у світі Java, з тією різницею, що кількість випадків, коли AOP дійсно вартує, ще більше обмежена.


Чи є довідкова URL-адреса до джерела інформації, де django використовує IoC?
Саджук

@Sajuuk, я дізнався, що про Джанго в нитці цього питання, тому я не знаю, вам слід попросити інших авторів відповіді.
zakmck

Перший alinea цієї відповіді додає на мою думку 0 значення ... Я думаю, що я здатний вирішити, коли мій пітон код виграє від IoC, і мені байдуже, що розробник вважає, що це погано. Я ціную прагматизм через необгрунтовану думку.
Майк де Клерк

@MikedeKlerk, я припускаю, що те, що є невідомим (як підтверджується безліч відповідей) та жертвою упередженості, навряд чи стане популярним, незалежно від того, наскільки об’єктивними та добре обізнаними є такі, як ти. І звичайно, я не впевнений, що це причина, чому ви не бачите багато використання IoC в Python, я вважаю, що головна причина в тому, що програми з низькою / середньою конкурентоспроможністю їх не потребують.
zakmck

The typical Python application is much simpler, just a bunch of scripts, without such a complex architecture.- цілком припущення
hyankov


-1

Я погоджуюся з @Jorg в тому, що DI / IoC можливий, простіший і ще красивіший в Python. Не вистачає рамки, яка її підтримує, але є кілька винятків. Навести кілька прикладів, які мені спадають на думку:

  • Зауваження Джанго дозволяють передавати свій власний клас коментарів за допомогою власної логіки та форм. [Більше інформації]

  • Django дозволяє використовувати спеціальний об'єкт профілю для приєднання до вашої моделі користувача. Це не зовсім IoC, але це хороший підхід. Особисто я хотів би замінити дірку Модель користувача, як це робиться в рамках коментарів. [Більше інформації]


-3

На мою думку, такі речі, як ін'єкція залежності, - це симптоми жорсткої та надскладної структури. Коли основна частина коду стає занадто вагомою, щоб легко змінюватись, вам виникає необхідність вибирати невеликі її частини, визначати для них інтерфейси, а потім дозволяти людям змінювати поведінку за допомогою об'єктів, що підключаються до цих інтерфейсів. Це все добре і добре, але краще уникати такої складності в першу чергу.

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

Як правило, я думаю, що люди Python звикли вибирати правильний інструмент для роботи, який є цілісним і простим цілим, а не Єдиним вірним інструментом (із тисячею можливих плагінів), який може робити все, що завгодно, але пропонує здивований масив можливих перестановок конфігурації . Все ще є взаємозамінні частини, де це необхідно, але без необхідності великого формалізму визначення фіксованих інтерфейсів, завдяки гнучкості введення качок та відносній простоті мови.


4
Це не стільки рамки, скільки сама мова. Щоб створити таку гнучкість, якою користуються мови набору качок, мовам, що мають статичний тип, потрібні дуже складні рамки та правила. DI - одне з цих правил. Люди з Python не думають про це двічі. Люди з Java повинні справді працювати над цим.
С.Лотт

6
@ S.Lott - Я б повністю погодився з вами, за винятком того, що люди C ++, схоже, обходять без вибуху дизайну та архітектури, незважаючи на роботу з аналогічними обмеженнями, ніж у Java. Я думаю, що це означає культурну різницю, коли, стикаючись з двома можливими способами зробити щось, люди Java вважають за краще витягти інший інтерфейс, щоб полегшити схему стратегії, тоді як люди C ++ занурюються прямо та додають заявку та заяву if
Kylotan

3
@Finglas, тож якщо у мене є десяток класів, які використовують усі мої EmailSenderта вирішили замінити його на DesktopNotifier, я повинен перейти і відредагувати 12 класів вручну. І ви вважаєте, що це простіше і зрозуміліше, якщо просто записати в INotifierінтерфейс і дати контейнеру розробити деталі?
Основні

1
На жаль, певний рівень складності - це реальність, з якою стикаються професійні розробники програмного забезпечення. Я бачу критику, але рішення у цій відповіді немає. Що є "пітонічним" рішенням цієї проблеми: я пишу бібліотеку і хочу надати гачок для ведення журналу (щось на зразок PSR-3 LoggerInterface PHP). Я знаю, як використовувати рівні журналів, але мені байдуже, як програма насправді звітує про них. Який чистий спосіб дозволити клієнтській програмі вводити цю деталь про реалізацію. Примітка: інші частини програми можуть мати різні реалізації цього інтерфейсу.
Роб

2
Моє запитання для вас полягає не в тому, як ви використовуєте стандартну бібліотеку журналів, а також не про створення різних примірників класу реєстратора. Моє запитання полягає в тому, як ви налаштуєте свою програму, щоб різні частини вашої програми могли використовувати різні реалізації, а не переймалися цими деталями (за умови, що вони знають, як використовувати інтерфейс). Це дуже реальна проблема, яку DI вирішив для декількох програм PHP, над якими я працював. Я шукаю пітонного еквівалента. І запропонувати "просто не робити вашу заявку такою складною" - це не відповідь, яку я шукаю.
Роб

-5

На відміну від сильної набраної природи на Яві. Поведінка качки Python дозволяє легко наводити об'єкти навколо.

Розробники Java роблять акцент на побудові структури класу та взаємозв'язку між об'єктами, зберігаючи гнучкість. IoC є надзвичайно важливим для досягнення цього.

Розробники Python зосереджуються на виконанні роботи. Вони просто проводять заняття, коли їм це потрібно. Вони навіть не повинні турбуватися про тип класу. Поки вона може стукати, це качка! Ця природа не залишає місця для IoC.


4
Ще потрібно знайти річ, яка брякне.
andho
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.