Dlaczego IoC / DI nie jest powszechne w Pythonie?

W Javie / DI jest bardzo powszechną praktyką, która jest szeroko stosowana w aplikacjach internetowych, prawie wszystkich dostępnych frameworkach i Java EE. Z drugiej strony, istnieje również wiele dużych aplikacji internetowych Pythona, ale oprócz Zope (który słyszałem, że powinien być naprawdę straszny do kodu) IoC nie wydaje się być bardzo powszechne w świecie Pythona. (Proszę podać kilka przykładów, jeśli uważasz, że się mylę).

Istnieje oczywiście kilka klonów popularnej Javy Frameworki IoC dostępne dla Pythona, na przykład springpython . Ale żaden z nich nie wydaje się być używany praktycznie. Przynajmniej nigdy nie natknąłem się na Django lub sqlalchemy+<insert your favorite wsgi toolkit here> oparta aplikacja internetowa, która używa czegoś takiego.

Moim zdaniem IoC ma rozsądne zalety i ułatwiłoby zastąpienie na przykład django-default-user-model, ale szerokie użycie klas interfejsu i IoC w Pythonie wygląda trochę dziwnie, a nie "pythonicznie". Ale może ktoś ma lepsze wyjaśnienie, dlaczego IoC nie jest szeroko stosowany w Pythonie.

Author: alexandrul, 2010-03-17

15 answers

Nie sądzę, aby DI / IoC były , Które są rzadkie w Pythonie. Czym jest nierzadko jednak są di / IoC Framework/containers .

Pomyśl o tym: co robi kontener DI? Pozwala na

  1. połącz niezależne komponenty w kompletną aplikację ...
  2. ... w czasie wykonywania.

Mamy nazwy dla "wiring together" I "at runtime":

  1. Skrypty
  2. dynamiczne

Więc, a DI kontener jest niczym innym jak interpreterem dynamicznego języka skryptowego. Powiem inaczej: typowy kontener Java/. NET DI to nic innego jak gówniany interpreter dla naprawdę kiepskiego dynamicznego języka skryptowego z brzydką, czasami opartą na XML składnią.

Kiedy programujesz w Pythonie, dlaczego chcesz używać brzydkiego, złego języka skryptowego, skoro masz do dyspozycji piękny, genialny język skryptowy? Właściwie to jest bardziej ogólne pytanie: kiedy programujesz w właściwie każdy język, dlaczego chcesz używać brzydkiego, złego języka skryptowego, skoro masz do dyspozycji Jython i IronPython?

Podsumowując: praktyka DI / IoC jest tak samo ważna w Pythonie, jak w Javie, z dokładnie tych samych powodów. Implementacja DI / IoC jest jednak wbudowana w Język i często jest tak lekka, że całkowicie znika.

(tutaj jest krótka analogia: w assembly, wywołanie podprogramu jest ładnym major deal - musisz zapisać swoje lokalne zmienne i rejestry do pamięci, zapisać gdzieś Swój adres zwrotny, zmienić wskaźnik instrukcji na podprogram, który wywołujesz, zorganizować, aby w jakiś sposób przeskoczył z powrotem do podprogramu, gdy jest skończony, umieścić argumenty gdzieś, gdzie callee może je znaleźć, i tak dalej. IOW: w assembly "wywołanie podprogramu" jest wzorcem projektowym, a zanim pojawiły się języki takie jak Fortran, które miały wbudowane wywołania podprogramów, ludzie budowali własne "podprogramy". Czy można powiedzieć, że wywołania podprogramów są "rzadkie" w Pythonie, tylko dlatego, że nie używasz frameworków podprogramów?)

BTW: dla przykładu, jak wygląda doprowadzenie DI do logicznego wniosku, spójrz na Gilad Bracha 'S Newspeak Programming Language i jego pisma na ten temat:

 204
Author: Jörg W Mittag,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-20 09:12:55

Częścią tego jest sposób, w jaki system modułów działa w Pythonie. Możesz uzyskać rodzaj "singleton" za darmo, po prostu importując go z modułu. Zdefiniuj rzeczywistą instancję obiektu w module, a następnie dowolny kod klienta może go zaimportować i faktycznie uzyskać działający, w pełni zbudowany / wypełniony obiekt.

Jest to w przeciwieństwie do Javy, gdzie nie importuje się rzeczywistych instancji obiektów. Oznacza to, że zawsze musisz je utworzyć samodzielnie (lub użyć jakiegoś stylu IoC / DI podejście). Możesz złagodzić problemy związane z koniecznością tworzenia instancji wszystkiego samodzielnie, mając statyczne metody fabryczne (lub rzeczywiste klasy fabryczne), ale wtedy nadal ponosisz koszty związane z faktycznym tworzeniem nowych za każdym razem.

 56
Author: TM.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-17 12:16:25

IoC I DI są bardzo powszechne w dojrzałym kodzie Pythona. Po prostu nie potrzebujesz frameworka do implementacji DI dzięki pisaniu kaczek.

Najlepszym przykładem jest to, jak skonfigurujesz aplikację Django używając 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 wykorzystuje 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

Przypomnę (źródło):

"Dependency Injection" jest 25-dolarowym terminem dla pojęcia 5 centów. [...] Dependency injection oznacza nadanie obiektowi zmiennych instancji. [...].

 55
Author: Max Malysh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-30 19:18:37

Django świetnie wykorzystuje inwersję sterowania. Na przykład serwer bazy danych jest wybierany przez plik konfiguracyjny, a następnie framework udostępnia klientom bazodanowym odpowiednie instancje wrappera bazy danych.

Różnica polega na tym, że Python ma typy pierwszej klasy. Typy danych, w tym klasy, same są obiektami. Jeśli chcesz, aby coś używało konkretnej klasy, po prostu nazwij ją. Na przykład:

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

Później kod może utworzyć interfejs bazy danych przez zapis:

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

Zamiast funkcji fabrycznych boilerplate, których potrzebują Java i C++, Python robi to za pomocą jednej lub dwóch linii zwykłego kodu. To jest siła programowania funkcyjnego kontra imperatywnego.

 34
Author: Daniel Newby,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-17 19:46:56

Widać, że ludzie naprawdę już nie rozumieją, co oznacza iniekcja zależności i inwersja kontroli.

Praktyką stosowania inwersji sterowania jest posiadanie klas lub funkcji, które zależą od innych klas lub funkcji, ale zamiast tworzyć instancje w kodzie klasy funkcji, lepiej jest otrzymać je jako parametr, więc luźne sprzężenie może być archieved. To ma wiele zalet, jak większa testowalność i archieve substytucji liskov zasada.

Widzisz, pracując z interfejsami i zastrzykami, Twój kod staje się bardziej konserwowalny, ponieważ możesz łatwo zmienić zachowanie, ponieważ nie będziesz musiał przepisywać ani jednej linii kodu (może jednej lub dwóch linii w konfiguracji DI) twojej klasy, aby zmienić jego zachowanie, ponieważ klasy implementujące interfejs, na który czeka twoja klasa, mogą różnić się niezależnie tak długo, jak podążają za interfejsem. Jedną z najlepszych strategii utrzymania kodu odsprzęgniętego i łatwego w utrzymaniu jest przestrzeganie co najmniej zasad jednostkowej odpowiedzialności, substytucji i inwersji zależności.

Do czego służy biblioteka DI, jeśli możesz utworzyć instancję obiektu wewnątrz pakietu i zaimportować go, aby wstrzyknąć go samodzielnie? Wybrana odpowiedź jest słuszna, ponieważ java nie ma sekcji proceduralnych (kodu poza klasami), wszystko, co idzie do nudnej konfiguracji XML, stąd potrzeba klasy do tworzenia instancji i wstrzykiwania zależności na leniwy sposób ładowania, więc nie zdmuchnąć wydajność, podczas gdy w Pythonie po prostu kodujesz iniekcje na" proceduralnych " (kod poza klasami) sekcjach kodu

 21
Author: jhonatan teixeira,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-09 04:27:48

Nie używałem Pythona od kilku lat, ale powiedziałbym, że ma on więcej wspólnego z tym, że jest dynamicznie pisanym językiem niż cokolwiek innego. Dla prostego przykładu, w Javie, gdybym chciał przetestować to coś napisanego do standardowego, mógłbym użyć DI i przekazać w dowolnym strumieniu wydruku, aby uchwycić napisany tekst i zweryfikować go. Kiedy jednak pracuję w Rubim, mogę dynamicznie zastąpić metodę 'puts' na STDOUT, aby wykonać weryfikację, pozostawiając DI całkowicie poza obrazem. Jeśli jedynym powodem, dla którego tworzę abstrakcję, jest przetestowanie używającej jej klasy (pomyśl o operacjach systemu plików lub zegarze w Javie), a następnie DI/IOC tworzy niepotrzebną złożoność rozwiązania.

 11
Author: bcarlso,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-17 12:10:16

Właściwie dość łatwo jest napisać wystarczająco czysty i zwarty kod za pomocą DI (zastanawiam się, czy będzie/pozostanie pythonic wtedy, ale tak czy siak :)), np. faktycznie perefer ten sposób kodowania:

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!

Tak, można to postrzegać jako prostą formę parametryzacji funkcji/klas, ale robi to swoje. Może domyślne baterie w Pythonie też tu wystarczą.

P. S. zamieściłem też większy przykład tego naiwnego podejścia na dynamiczna analiza prostej logiki logicznej w Pythonie.

 10
Author: mlvljr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:43

IoC / DI jest koncepcją projektową, ale niestety często jest traktowana jako koncepcja, która odnosi się do niektórych języków (lub systemów typowania). Chciałbym, aby kontenery dependency injection stały się znacznie bardziej popularne w Pythonie. Jest wiosna, ale to super-framework i wydaje się być bezpośrednim portem pojęć Javy bez większego uwzględnienia " sposobu Pythona."

Biorąc pod uwagę adnotacje w Pythonie 3, zdecydowałem się na pęknięcie w pełni funkcjonalnym, ale prostym kontenerze iniekcji zależności: https://github.com/zsims/dic . Jest oparty na niektórych koncepcjach z kontenera. NET dependency injection (co IMO jest fantastyczne, jeśli kiedykolwiek grasz w tej przestrzeni), ale zmutowany z pojęciami Pythona.

 9
Author: zsims,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-24 06:03:24

Myślę, że ze względu na dynamiczną naturę Pythona ludzie nie często widzą potrzebę innego dynamicznego frameworka. Gdy klasa dziedziczy z 'obiektu' w Nowym stylu, możesz utworzyć nową zmienną dynamicznie ( https://wiki.python.org/moin/NewClassVsClassicClass).

I. E. W Pythonie prostym:

#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()

Jednak spójrz na https://github.com/noodleflake/pyioc to może być to, czego szukasz.

Tj. w pyioc

from libs.service_locator import ServiceLocator

#main.py
ServiceLocator.register(PostgresConnection)

#other.py
postgres_connection = ServiceLocator.resolve(PostgresConnection)
db_data = postgres_connection.fetchone()
 7
Author: Martin Swanepoel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-28 14:19:51

Popieram" Jörg W Mittag "odpowiedź:" implementacja di/IoC w Pythonie jest tak lekka, że całkowicie znika".

Aby utworzyć kopię zapasową tego stwierdzenia, spójrz na słynny przykład Martina Fowlera przeniesiony z Javy do Pythona: Python:Design_Patterns: Inversion_of_Control

Jak widać z powyższego linku, "kontener" w Pythonie może być napisany w 8 linijkach kodu:

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
 5
Author: emilmont,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-20 10:33:52

Moje 2cents jest to, że w większości aplikacji Pythona nie potrzebujesz go i, nawet jeśli go potrzebujesz, jest szansa, że wielu hejterów Javy (i niekompetentnych skrzypków, którzy wierzą, że są programistami) uważa go za coś złego, tylko dlatego, że jest popularny w Javie.

System IoC jest w rzeczywistości przydatny, gdy masz złożone sieci obiektów, gdzie każdy obiekt może być zależny od kilku innych i, z kolei, być sam w sobie zależny od innych obiektów. W takim przypadku będziesz chciał zdefiniować wszystkie te obiekty raz i mają mechanizm automatycznego łączenia ich w całość, oparty na jak największej liczbie ukrytych reguł. Jeśli masz również konfigurację, która ma być zdefiniowana w prosty sposób przez użytkownika/administratora aplikacji, jest to dodatkowy powód, aby chcieć systemu IoC, który może odczytywać swoje komponenty z czegoś w rodzaju prostego pliku XML (który byłby konfiguracją).

Typowa aplikacja Pythona jest znacznie prostsza, tylko kilka skryptów, bez tak złożonej architektury. Osobiście jestem świadomy tego, czym jest IoC (w przeciwieństwie do tych, którzy napisali tutaj pewne odpowiedzi) i nigdy nie czułem potrzeby tego w moim ograniczonym doświadczeniu w Pythonie(również nie używam Springa wszędzie, nie wtedy, gdy zalety, które daje, nie uzasadniają jego rozwoju).

To powiedziawszy, są sytuacje Pythona, w których podejście IoC jest rzeczywiście użyteczne i, w rzeczywistości, czytałem tutaj, że Django go używa.

To samo rozumowanie powyżej można by zastosować do aspektu zorientowanego Programowanie w świecie Javy, z tą różnicą, że liczba przypadków, w których AOP naprawdę się opłaca, jest jeszcze bardziej ograniczona.

 2
Author: zakmck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-09 13:44:31

Pytest optimization all based on DI (source)

 1
Author: Meng Zhao,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-11 18:31:37

Zgadzam się z @Jorg w punkcie, że DI / IoC jest możliwe, łatwiejsze i jeszcze piękniejsze w Pythonie. Brakuje tylko frameworków ją obsługujących, ale jest kilka wyjątków. Aby wskazać kilka przykładów, które przychodzą mi do głowy:

  • Komentarze Django pozwalają Ci połączyć własną klasę komentarzy z Twoją niestandardową logiką i formularzami. [Więcej Informacji]

  • Django pozwala ci użyć obiektu niestandardowego profilu do dołączenia do twojego modelu użytkownika. Nie jest to całkowicie IoC, ale jest dobre podejście. Osobiście chciałbym zastąpić model użytkownika otworu tak jak robi to framework komentarzy. [Więcej Informacji]

 -1
Author: santiagobasulto,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-20 19:39:36

Moim zdaniem takie rzeczy jak iniekcja zależności są objawami sztywnych i nadmiernie złożonych ram. Kiedy główny kod staje się zbyt ciężki, aby łatwo go zmienić, musisz wybrać małe jego części, zdefiniować dla nich interfejsy, a następnie pozwolić ludziom na zmianę zachowania za pomocą obiektów, które podłączają się do tych interfejsów. To wszystko dobrze i dobrze, ale lepiej unikać tego rodzaju złożoności w pierwszej kolejności.

To także objaw język pisany statycznie. Kiedy jedynym narzędziem do wyrażania abstrakcji jest dziedziczenie, to jest to prawie to, czego używasz wszędzie. Mimo to, C++ jest dość podobny, ale nigdy nie podniósł fascynacji budowniczych i interfejsów wszędzie, że programiści Java zrobił. Łatwo jest przebrnąć przez-żywiołowy sen o elastyczności i możliwości rozbudowy kosztem napisania o wiele za dużo kodu generycznego z niewielką rzeczywistą korzyścią. Myślę, że to kulturowe rzecz.

Zazwyczaj myślę, że ludzie Pythona są przyzwyczajeni do wybierania odpowiedniego narzędzia do pracy, które jest spójną i prostą całością, a nie jedynym prawdziwym narzędziem (z tysiącem możliwych wtyczek), które może zrobić wszystko, ale oferuje oszałamiającą gamę możliwych permutacji konfiguracji. Tam, gdzie to konieczne, nadal istnieją wymienne części, ale bez potrzeby dużego formalizmu definiowania stałych interfejsów, ze względu na elastyczność typowania kaczek i względną prostotę język.

 -4
Author: Kylotan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-17 11:41:20

W przeciwieństwie do strong typed nature w Javie. Pisanie kaczek w Pythonie sprawia, że przekazywanie obiektów jest tak łatwe.

Programiści Javy skupiają się na konstruowaniu klasy strcuture i relacji między obiektami, zachowując jednocześnie elastyczność. MKOl jest niezwykle ważny dla osiągnięcia tego celu.

Programiści Pythona skupiają się na wykonaniu pracy. Po prostu włączają zajęcia, kiedy tego potrzebują. Nie muszą się nawet martwić o Typ klasy. Tak długo, jak to can quack, to kaczka! Ta natura nie pozostawia miejsca dla MKOl.

 -6
Author: Jason Ching,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-06 13:10:24