Dlaczego IoC / DI nie jest powszechne w Pythonie?

312

W Javie IoC / 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 w Pythonie, ale oprócz Zope (które, jak słyszałem, powinno być naprawdę okropne w kodowaniu), IoC nie wydaje się bardzo powszechne w świecie Pythona. (Proszę podać kilka przykładów, jeśli uważasz, że się mylę).

Istnieje oczywiście kilka klonów popularnych frameworków Java IoC dla Pythona, na przykład springpython . Ale żaden z nich nie wydaje się przyzwyczajony praktycznie. Przynajmniej nigdy nie natknąłem się na aplikację internetową opartą na Django lub sqlalchemy + <insert your favorite wsgi toolkit here>, która używa czegoś takiego.

Moim zdaniem IoC ma rozsądne zalety i ułatwiłoby na przykład zastąpienie domyślnego modelu użytkownika django, ale szerokie użycie klas interfejsów i IoC w Pythonie wygląda nieco dziwnie, a nie »pythonowo«. Ale może ktoś ma lepsze wytłumaczenie, dlaczego IoC nie jest powszechnie używany w Pythonie.

tux21b
źródło
2
Sądzę, że z tego samego powodu, że jest mniej popularny w Ruby, wbudowanych mixinach i klasach otwartych
Sam Saffron
3
próbowałeś kiedyś Springpython? to nawet nie działa zgodnie z reklamą. przynajmniej w części AOP. wszystko inne tam nie jest bardzo przydatne, chyba że pochodzisz z javy i potrzebujesz pewnego poziomu komfortu podczas przejścia.
Tom Willis,
6
Zachowaj ostrożność, aby odróżnić użycie DI od korzystania z ram MKOl. Pierwsza z nich jest wzorcem projektowym, druga stanowi ramy wspomagające automatyczne korzystanie z pierwszej.
Doug
Doug, uważam, że chciałeś powiedzieć, że DI to funkcja kreacji uzyskana przy użyciu wzoru Dekoratora.
njappboy
4
Chciałbym zobaczyć odpowiedź, która rozwiązuje rzeczywiste problemy, które rozwiązuje DI: zarządzanie przez całe życie, łatwość testowania itp. Jeśli istnieje bardziej Pythoniczny sposób na rozwiązanie tych problemów, jestem na poważnie.
Josh Noe

Odpowiedzi:

197

Nie sądzę, że DI / IoC są tak rzadkie w Pythonie. Co jest rzadkością są jednak DI / IoC Ramki / pojemników .

Pomyśl o tym: co robi pojemnik DI? To pozwala

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

Mamy nazwy „łączenie razem” i „w czasie wykonywania”:

  1. skryptowanie
  2. dynamiczny

Tak więc kontener DI jest niczym innym jak tłumaczem dla dynamicznego języka skryptowego. Właściwie to pozwolę sobie sformułować inaczej: typowy kontener Java / .NET DI to nic innego, jak kiepski interpreter dla naprawdę złego dynamicznego języka skryptowego z brzydką, czasem opartą na XML składnią.

Kiedy programujesz w Pythonie, dlaczego miałbyś chcieć używać brzydkiego, złego języka skryptowego, skoro masz do dyspozycji piękny, genialny język skryptowy? W rzeczywistości jest to bardziej ogólne pytanie: kiedy programujesz w prawie dowolnym języku, dlaczego miałbyś chcieć 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. Realizacja DI / IoC jednak jest wbudowana w języku i często tak lekki, że całkowicie znika.

(Oto krótkie omówienie analogii: w asemblerze wywołanie podprogramu jest dość ważną sprawą - musisz zapisać lokalne zmienne i rejestry w pamięci, gdzieś zapisać swój adres zwrotny, zmienić wskaźnik instrukcji na podprogram, który wywołujesz, zorganizuj, aby w jakiś sposób wskoczył z powrotem do podprogramu, gdy zostanie ukończony, umieść argumenty w miejscu, w którym odbiorca może je znaleźć, itd. IOW: w asemblerze „wywołanie podprogramu” jest wzorcem projektowym, a wcześniej istniały języki takie jak Fortran, który miał wbudowane wywołania podprogramów, ludzie budowali własne „ramy podprogramów”. Czy powiedziałbyś, że wywołania podprogramów są „rzadkie” w Pythonie, tylko dlatego, że nie używasz ram podprogramów?)

BTW: na przykład jak to wygląda wziąć DI do logicznego wniosku, przyjrzeć Gilad Bracha „s nowomowy języka programowania i jego pism na ten temat:

Jörg W Mittag
źródło
58
Chociaż się zgadzam. Komentarz XML jest niepoprawny. Wiele (przynajmniej nowoczesnych) kontenerów IOC korzysta z konwencji (kodu) nad konfiguracją (XML).
Finglas
20
Nic nie stoi na przeszkodzie, aby napisać okablowanie jawnie w Javie, ale ponieważ masz coraz więcej usług, zależności stają się bardziej złożone. Kontener DI jest podobny do Make: deklarujesz zależności, a kontener inicjuje je we właściwej kolejności. Guice to framework Java DI, w którym wszystko jest napisane w kodzie Java. Pisząc deklaratywnie, kontener DI dodaje także obsługę przetwarzania końcowego deklinacji przed inicjalizacją (np. Zamienia symbole zastępcze właściwości na wartości rzeczywiste)
IttayD
133
„Implementacja DI / IoC jest jednak wbudowana w język i często tak lekka, że ​​całkowicie znika”. Głosuj w dół, ponieważ kategorycznie jest to nieprawda. DI to wzorzec, w którym interfejs jest przekazywany do konstruktora. Nie jest wbudowany w Pythona.
Doug
146
downvote, łączenie okablowania nie ma nic wspólnego ze skryptami, DI jest wzorcem i nie jest równoważne ze skryptami
Luxspes 23.12.12
38
Nie zgadzam się z tym. DI nie rozwiązuje problemu braku dynamicznych skryptów w językach statycznych. Zapewnia platformę do konfigurowania i komponowania części aplikacji. Kiedyś słyszałem, jak deweloper Ruby powiedział, że DI jest niepotrzebny w dynamicznych językach. Ale użył Railsów ... Railsy to po prostu duży kontener DI, który wykorzystuje konwencję, aby dowiedzieć się, które części skonfigurować. Nie potrzebował DI, ponieważ Rails rozwiązał problem znalezienia części dla niego.
Brian Genisio,
51

Częściowo jest to sposób, w jaki system modułowy działa w języku Python. Możesz uzyskać swego rodzaju „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, w której nie importujesz rzeczywistych wystąpień obiektów. Oznacza to, że zawsze musisz sam utworzyć ich instancję (lub zastosować jakieś podejście oparte na stylu IoC / DI). Możesz złagodzić problem związany z koniecznością tworzenia wszystkiego samodzielnie, stosując statyczne metody fabryczne (lub rzeczywiste klasy fabryczne), ale wtedy nadal ponosisz nakłady związane z tworzeniem nowych za każdym razem.

TM.
źródło
2
To ma sens. Jeśli chcę zmienić implementację w Pythonie, po prostu importuję z innej lokalizacji, używając tej samej nazwy. Ale teraz myślę, czy jest to również możliwe na odwrót, definiując MyClassInstancesklasę dla każdej z nich MyClassw Javie, która zawiera tylko statyczne, w pełni zainicjowane instancje. To byłoby okablowane: D
tux21b
2
I jeszcze jeden pomysł: Zapewnienie sposobu zmiany takiego importu w Pythonie umożliwiłoby łatwą wymianę implementacji bez dotykania wszystkich plików Pythona. Zamiast from framework.auth.user import User tego lepiej byłoby pisać User = lookup('UserImplentation', 'framework.auth.user.User')(drugi parametr może być wartością domyślną) wewnątrz frameworka. Wtedy użytkownicy frameworka byliby w stanie zastąpić / specjalizować Userimplementację bez dotykania frameworka.
tux21b,
14
Upraszczanie, odpowiedź, w rzeczywistości rzadko potrzebujesz po prostu „singletona”, musisz kontrolować zakres (możesz potrzebować wątku lokalnego singletona lub singletona sesji itd.), To sprawia, że ​​myślę, że tego rodzaju problemy rozwiązane w Pythonie nie są rzeczywistymi problemami rozwiązanymi w środowisku korporacyjnym
Luxspes 23.12.12
3
W rzeczywistości DI polega na możliwości testowania i rozdzielania zależności kodu. Również funkcja importu jest podobna do importu statycznego w Javie, co pozwala mi zaimportować pojedyncze wystąpienie obiektu.
Richard Warburton
1
„Możesz uzyskać coś w rodzaju„ singletonu ”za darmo, po prostu importując go z modułu.” Można łatwo zrobić w Javie, deklarując pole instancji statycznej i ustawiając wartość. To nie jest sol
ggranum
45

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 konfiguracja aplikacji Django przy użyciu 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 mocno 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

Pozwól, że przypomnę ( źródło ):

„Wstrzykiwanie zależności” to termin 25 dolarów za koncepcję 5 centów. [...] Wstrzykiwanie zależności oznacza nadanie obiektowi zmiennych instancji. [...]

Max Malysh
źródło
8
+1. Dobrze wyłożone. Będąc programistą Python całkowicie zaskoczyła mnie prezentacja całego wywiadu na temat platform DI w języku C #. Trochę mi zajęło uświadomienie sobie, że robiłem to cały czas w aplikacjach Flask, nawet o tym nie myśląc, ponieważ nie potrzebujesz frameworka. Dla kogoś, kto nie wie nic poza C # / Java, pytanie ma sens. Dla programistów języka piszących na klawiaturze jest to po prostu naturalne i, jak mówisz, „25-dolarowy termin na 5-centową koncepcję”.
Samuel Harmer,
5
err ... to nie jest wstrzykiwanie zależności, ponieważ instancje ( IsAuthenticated, ScopedRateThrottle) są tworzone przez klasę. Nie są przekazywane do konstruktora.
dopatraman
5
IsAuthenticatedi ScopedRateThrottlenie są instancjami, to są klasy. Są one tworzone podczas tworzenia FooView (w rzeczywistości, gdy FooView przetwarza żądanie). W każdym razie jest to jedynie szczegół implementacji. IsAuthenticatedi ScopedRateThrottlesą zależnościami; są wstrzykiwane do FooView. Nie ma znaczenia, kiedy i jak to się robi. Python nie jest Javą, więc istnieją różne sposoby implementacji tego.
Max Malysh,
3
@MaxMalysh Zgadzam się z dopatraman w tej sprawie. To nie jest nawet IoC, ponieważ sama klasa ma „zakodowane” zależności od konkretnej klasy. W IoC zależność należy podać zamiast na stałe. Ponadto w Dependency Injection będziesz mieć podmiot odpowiedzialny za zarządzanie cyklem życia każdej usługi i wstrzykiwanie ich w takim przypadku. Rozwiązanie podane w żadnym z nich.
Ricardo Alves,
3
@alex Nie, nie musisz zmieniać kodu, aby użyć innego mechanizmu renderującego. Można nawet korzystać z wielu renderujące jednocześnie: renderer_classes = (JSONRenderer, BrowsableAPIRenderer, XMLRenderer). Wyśmiewanie jest tak proste jak @unittest.patch('myapp.views.FooView.permission_classes'). Rozpaczliwa potrzeba „przekazania czegoś” jest konsekwencją „Java robienia rzeczy”, ponieważ Java jest skompilowanym i statycznie typowanym językiem pozbawionym silnych możliwości metaprogramowania.
Max Malysh
35

Django doskonale wykorzystuje inwersję kontroli. Na przykład serwer bazy danych jest wybierany przez plik konfiguracyjny, a następnie środowisko udostępnia odpowiednie instancje opakowania bazy danych klientom bazy danych.

Różnica polega na tym, że Python ma typy pierwszej klasy. Typy danych, w tym klasy, same są obiektami. Jeśli chcesz użyć określonej 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 następnie utworzyć interfejs bazy danych, pisząc:

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

Zamiast funkcji fabrycznych, których potrzebują Java i C ++, Python robi to z jednym lub dwoma wierszami zwykłego kodu. Jest to siła programowania funkcjonalnego i imperatywnego.

Daniel Newby
źródło
4
To, co nazywacie kodem, jest w rzeczywistości częścią okablowania. To byłby kod XML twojego środowiska ioc. Można go napisać po prostu jako import psycopg2 as database_interface. Umieść tę linię w innym miejscu injections.py.
spectras
29
Erm. To, co tam robisz, jest w zasadzie podręcznikiem Daniela.
Shayne,
Jest to zdecydowanie kod niezbędny, ale jest funkcjonalny, ponieważ używa wartości wywoływanej jako wartości.
Jeremy
5
Czy to jednak nie tylko funkcje pierwszej klasy? en.wikipedia.org/wiki/First-class_function Tylko dlatego, że je masz i używasz, nie czyni twojego kodu funkcjonalnym. Dzieje się tutaj sporo efektów ubocznych (takich jak zmiana self.database_interface), które krzyczą koniecznie.
hjc1710,
15

Widać, że ludzie naprawdę nie rozumieją już, co oznacza zastrzyk zależności i odwrócenie kontroli.

Praktyką użycia odwracania kontroli jest posiadanie klas lub funkcji, które zależą od innych klas lub funkcji, ale zamiast tworzenia instancji z klasy kodu funkcji lepiej jest otrzymać go jako parametr, aby można było zarchiwizować luźne sprzężenie. Ma to wiele zalet, takich jak większa testowalność i zarchiwizowanie zasady substytucji Liskova.

Widzisz, pracując z interfejsami i zastrzykami, twój kod staje się łatwiejszy w utrzymaniu, ponieważ możesz łatwo zmienić zachowanie, ponieważ nie będziesz musiał przepisywać ani jednego wiersza kodu (może jednego lub dwóch w konfiguracji DI) swojego klasa, aby zmienić swoje zachowanie, ponieważ klasy, które implementują interfejs, na który czeka klasa, mogą się różnić niezależnie, o ile podążają za interfejsem. Jedną z najlepszych strategii utrzymania oddzielenia kodu i łatwości jego utrzymania jest przestrzeganie co najmniej jednej zasady odpowiedzialności, podstawiania i inwersji zależności.

Do czego służy biblioteka DI, jeśli możesz samodzielnie utworzyć instancję obiektu w pakiecie i zaimportować go, aby sam go wstrzyknąć? Wybrana odpowiedź jest słuszna, ponieważ java nie ma sekcji proceduralnych (kod poza klasami), wszystko to idzie w nudne konfiguracyjne pliki XML, stąd potrzeba klasy do tworzenia instancji i wstrzykiwania zależności w sposób leniwy, abyś nie zdmuchnął wydajność, podczas gdy w Pythonie po prostu kodujesz zastrzyki w sekcjach „proceduralnych” (kod poza klasami) twojego kodu

jhonatan teixeira
źródło
wciąż tęsknisz za tym, że IoC / DI automatycznie łączy ze sobą obiekty. Niewiele jest w stanie to zrobić w środowisku uruchomieniowym (Java i tak może to zrobić przez odbicie), chodzi o to, że środowisko zajmuje się tym i nie trzeba tego robić jawnie. Posiadanie sekcji proceduralnych również nie ma znaczenia, nic nie stoi na przeszkodzie, aby napisać całkowicie proceduralną aplikację w Javie, wykorzystując klasy jako zwykłe kontenery statycznych podprogramów i funkcji, bez korzystania z funkcji OOP.
zakmck
@zakmck: sekcja „proceduralna” Pythona tutaj tak naprawdę nie dotyczy pisania kodu proceduralnego. Tym, co odróżnia sekcję „proceduralną” języka Python od języków statycznych, jest możliwość umieszczania kodu proceduralnego w ciele klasy, który działa w czasie definicji klasy, umieszczania instrukcji importu w instrukcji if oraz tworzenie fabryki klas po prostu poprzez zdefiniowanie klas w metodzie fabrycznej. Są to rzeczy, których tak naprawdę nie można zrobić w językach statycznych i które rozwiązują większość problemów, które próbowały rozwiązać IOC / DI. Metaprogramowanie w Pythonie często wygląda jak zwykły kod Pythona.
Lie Ryan,
@LieRyan, możesz to zrobić za pomocą refleksji lub, jeśli potrzebujesz go często lub w czasie wykonywania, możesz wywoływać język statyczny z innego języka, takiego jak Groovy (który jest zaprojektowany do łatwej gry w Javie), a nawet samego Pythona. Ma to jednak niewiele wspólnego ze strukturami IoC / DI, ponieważ ich celem jest automatyczne wykonanie większości okablowania obiektów proceduralnych, wykorzystując tylko definicje. Niestety w większości odpowiedzi udzielonych w niniejszym dokumencie brakuje tego punktu.
zakmck
12

Nie używałem Pythona od kilku lat, ale powiedziałbym, że ma on więcej wspólnego z tym, że jest to język dynamicznie pisany niż cokolwiek innego. Dla prostego przykładu, w Javie, gdybym chciał przetestować, czy coś napisało się odpowiednio do standardu, mógłbym użyć DI i przekazać dowolny PrintStream, aby przechwycić napisany tekst i go zweryfikować. Jednak kiedy pracuję w Ruby, mogę dynamicznie zastępować metodę „puts” na STDOUT w celu przeprowadzenia weryfikacji, pozostawiając DI całkowicie poza obrazem. Jeśli jedynym powodem, dla którego tworzę abstrakcję, jest przetestowanie klasy, która jej używa (pomyśl operacje systemu plików lub zegar w Javie), wtedy DI / IoC powoduje niepotrzebną złożoność rozwiązania.

bcarlso
źródło
3
Nigdy nie przestaje mnie dziwić, że ludzie chcą zmienić sposób działania systemu, aby przetestować jego działanie. Teraz musisz sprawdzić, czy twoje testy nie powodują skutków ubocznych.
Podstawowy
2
mówi o zmianie metody put tylko w zakresie testowym, to jest jak próbna metoda wstrzykiwanego obiektu.
dpa
2
@Podstawowe, co jest dość normalne w testach jednostkowych , w rzeczywistości jest wskazane, aby zrobić to w tych testach, ponieważ nie chcesz zanieczyszczać pokrycia swojego przypadku testowego więcej niż jednym blokiem kodu (tym, który jest testowany). Błędem byłoby to robić w przypadku testów integracyjnych, może to, o czym mówisz w swoim komentarzu?
samuelgrigolato
1
Dla mnie testowalność jest kwestią pierwszorzędną. Jeśli projekt nie jest testowalny, nie jest to dobry projekt i nie mam problemu ze zmianą projektu, aby był bardziej testowalny. Będę musiał ponownie sprawdzić, czy nadal działa, ale to jest w porządku. Testowalność jest całkowicie uzasadnionym powodem do zmiany kodu IMO
Carlos Rodriguez
10

Właściwie dość łatwo jest napisać wystarczająco czysty i kompaktowy kod za pomocą DI (zastanawiam się, czy będzie to / pozostanie pythoniczny , ale mimo wszystko :)), na przykład właściwie wybrałem 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 działa. Może więc domyślne baterie Pythona też tu wystarczą.

PS Zamieściłem również większy przykład tego naiwnego podejścia podczas Dynamicznej oceny prostej logiki logicznej w Pythonie .

mlvljr
źródło
3
Dla prostych przypadków, które mogą działać, ale wyobraź sobie prosty kontroler blogów internetowych, który wykorzystuje różne modele (post, komentarz, użytkownik). Jeśli chcesz, aby użytkownik wstrzyknął swój własny model Post (z dodatkowym atrybutem liczby wyświetleń do śledzenia tego) oraz swój własny model użytkownika z większą ilością informacji o profilu itd., Wszystkie parametry mogą wyglądać na mylące. Dodatkowo użytkownik może chcieć zmienić obiekt Request, aby obsługiwał sesję systemu plików zamiast zwykłej sesji opartej na plikach cookie lub coś w tym rodzaju ... Tak więc wkrótce otrzymasz wiele parametrów.
tux21b,
1
@ tux21b No cóż, istnieje „zasadnicza złożoność”, którą użytkownicy chcą wdrożyć, istnieją rozwiązania architektoniczne (niektóre z nich niegorsze od pozostałych pod względem rozwoju i ewentualnie czasu konserwacji, szybkości wykonania itp. ) i istnieje ludzka zdolność do zrozumienia interfejsu API i architektury oprogramowania. Jeśli nie ma żadnego zrozumiałego dla człowieka rozwiązania (nie tylko wśród tych, którzy używają (jakiejkolwiek formy) DI) ... cóż, kto powiedział, że wszystkie problemy można rozwiązać? A posiadanie wielu domyślnie przypisanych (ale możliwych do zamiany parametrów) parametrów może w rzeczywistości często wystarczyć.
mlvljr
9

IoC / DI jest koncepcją projektową, ale niestety jest często traktowana jako koncepcja, która dotyczy niektórych języków (lub systemów pisania). Chciałbym, aby pojemniki do wstrzykiwania zależności stały się znacznie bardziej popularne w Pythonie. Jest Spring, ale to super-framework i wydaje się być bezpośrednim portem pojęć Java bez większego zastanowienia się nad „The Python Way”.

Biorąc pod uwagę Adnotacje w Pythonie 3, zdecydowałem się na crack w pełni funkcjonalnym, ale prostym kontenerze wstrzykiwania zależności: https://github.com/zsims/dic . Opiera się na niektórych koncepcjach z kontenera wstrzykiwania zależności .NET (który IMO jest fantastyczny, jeśli kiedykolwiek grasz w tej przestrzeni), ale zmutowany z koncepcjami Pythona.

zsims
źródło
6

Myślę, że ze względu na dynamiczny charakter pythonów ludzie często nie widzą potrzeby stosowania innych dynamicznych ram. Gdy klasa dziedziczy po „obiekcie” nowego stylu, możesz dynamicznie utworzyć nową zmienną ( https://wiki.python.org/moin/NewClassVsClassicClass ).

tzn. w zwykłym pythonie:

#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()
Martin Swanepoel
źródło
2
Sam fakt, że obie wersje wykorzystują tę samą ilość kodu, znacznie przyczynia się do wyjaśnienia, dlaczego użycie frameworka nie jest zbyt popularne.
spectras
W other.pylinii 1 znajduje się automatyczne rozwiązywanie zależności, ale nie liczyłoby się to jako zastrzyk zależności.
andho
Lokalizatory usług są zwykle anty-wzorcem, po prostu mówiąc.
PmanAce
6

Odpowiadam „Jörg W Mittag”: „Implementacja DI / IoC w Pythonie jest tak lekka, że ​​całkowicie znika”.

Aby wykonać kopię zapasową tego oświadczenia, spójrz na przykład słynnego Martina Fowlera przeniesionego z Javy do Pythona: Python: Design_Patterns: Inversion_of_Control

Jak widać z powyższego linku, „Kontener” w Pythonie można zapisać w 8 wierszach 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
Emilmont
źródło
42
To znacznie odbiega nawet od najsłabszych pojemników DI. Gdzie jest zarządzanie przez całe życie, rekurencyjne rozwiązywanie zależności, zdolność kpienia lub - w przypadku braku tego - konfiguracja? To nic innego jak wyszukiwanie typu i pamięć podręczna, co nie jest tym samym, co IoC.
Podstawowy
2
Wiele lat temu napisałem małą platformę DI wykorzystującą metaklasy jako ćwiczenie. Całość stanowi pojedynczy plik z zerowym importem i dokumentami, które sprawiają, że jest to oczywiste. Pokazuje, że podstawowe funkcje nie są tak trudne do wdrożenia w sposób, który jest nawet „pythonowy”, ale szczerze myślę, że to smutne, że żadne kompletne rozwiązanie nie zyskało tak dużej przyczepności jak Spring w Javie i wszyscy robią niestandardowe architektury wtyczek.
Andrea Ratto,
2

Moim 2 centami jest to, że w większości aplikacji Pythona nie potrzebujesz go, a nawet jeśli go potrzebujesz, są szanse, że wielu hejterów Javy (i niekompetentnych skrzypków, którzy uważają się za programistów) uważają to za coś złego, tylko dlatego, że jest popularne w Javie .

System IoC jest faktycznie przydatny, gdy masz złożone sieci obiektów, w których każdy obiekt może być zależny od kilku innych, a z kolei sam być zależny od innych obiektów. W takim przypadku będziesz chciał zdefiniować wszystkie te obiekty raz i mieć mechanizm automatycznego ich łączenia, w oparciu o jak najwięcej domyślnych reguł. Jeśli masz również konfigurację, którą użytkownik / administrator aplikacji może zdefiniować w prosty sposób, jest to dodatkowy powód, dla którego pragniesz systemu IoC, który może czytać jego komponenty z czegoś takiego jak prosty plik XML (który byłby konfiguracją).

Typowa aplikacja w języku Python jest znacznie prostsza, to tylko kilka skryptów, bez tak złożonej architektury. Osobiście jestem świadomy tego, czym tak naprawdę jest IoC (w przeciwieństwie do tych, którzy napisali tutaj pewne odpowiedzi) i nigdy nie czułem takiej potrzeby w moim ograniczonym doświadczeniu w Pythonie (także nie używam Spring wszędzie, nie kiedy korzyści daje nie uzasadnia narzutu na rozwój).

To powiedziawszy, istnieją sytuacje w Pythonie, w których podejście IoC jest rzeczywiście przydatne i faktycznie przeczytałem tutaj, że Django go używa.

Takie samo rozumowanie można zastosować do programowania aspektowego w świecie Java, z tą różnicą, że liczba przypadków, w których AOP jest naprawdę opłacalny, jest jeszcze bardziej ograniczona.

zakmck
źródło
Czy istnieje referencyjny adres URL źródła informacji, w których django używa IoC?
Sajuuk,
@Sajuuk, dowiedziałem się o Django w wątku tego pytania, więc nie wiem, powinieneś zapytać innych autorów odpowiedzi.
zakmck,
Pierwszy alinea tej odpowiedzi dodaje moim zdaniem 0 wartości ... Myślę, że jestem w stanie zdecydować, kiedy mój kod python skorzysta z IoC, i nie dbam o to, co programista myśli, co jest złe. Cenię pragmatyzm nad nieuzasadnionymi opiniami.
Mike de Klerk
@MikedeKlerk moja sugestia jest taka, że ​​coś, co jest zarówno nieznane (jak potwierdza wiele odpowiedzi tutaj), jak i ofiara przesądów, nie stanie się popularne, bez względu na to, jak obiektywny i dobrze poinformowany jest kilka osób takich jak ty. I oczywiście nie jestem pewien, czy to jest powód, dla którego nie widzisz wielu zastosowań IoC w Pythonie, myślę, że głównym powodem jest to, że aplikacje o niskiej / średniej współzależności nie potrzebują ich.
zakmck
The typical Python application is much simpler, just a bunch of scripts, without such a complex architecture.- całkiem założenie
hyankov
1

pytest osprzęt wszystkie oparte na DI ( źródło )

Meng Zhao
źródło
-1

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

  • Komentarze Django pozwalają połączyć własną klasę Comment z własną logiką i formularzami. [Więcej informacji]

  • Django pozwala używać niestandardowego obiektu profilu do dołączenia do modelu użytkownika. To nie jest całkowicie IoC, ale jest to dobre podejście. Osobiście chciałbym zastąpić model User hole, tak jak robi to struktura komentarzy. [Więcej informacji]

Santiagobasulto
źródło
-3

Moim zdaniem rzeczy takie jak wstrzykiwanie zależności są objawami sztywnego i nadmiernie złożonego środowiska. Kiedy główna część kodu staje się zbyt ciężka, aby łatwo ją zmienić, musisz wybrać jej małe części, zdefiniować dla nich interfejsy, a następnie pozwolić ludziom na zmianę zachowania za pomocą obiektów podłączanych do tych interfejsów. To wszystko dobrze i dobrze, ale przede wszystkim lepiej unikać tego rodzaju złożoności.

Jest to również objaw języka o typie statycznym. Gdy jedynym narzędziem, które musisz wyrazić abstrakcją, jest dziedziczenie, to właściwie tego używasz wszędzie. Powiedziawszy to, C ++ jest dość podobny, ale nigdy nie zauważył fascynacji programami budującymi i interfejsami wszędzie tam, gdzie robili to deweloperzy Java. Łatwo jest być nadmiernie żywiołowym, marząc o elastyczności i rozszerzalności kosztem pisania zbyt dużej ilości ogólnego kodu przy niewielkich rzeczywistych korzyściach . Myślę, że to kwestia kulturowa.

Zazwyczaj myślę, że ludzie Python są przyzwyczajeni do wybierania odpowiedniego narzędzia do zadania, które jest spójną i prostą całością, zamiast Jednego Prawdziwego Narzędzia (Z Tysiącem Możliwych Wtyczek), które może zrobić wszystko, ale oferuje oszałamiającą gamę możliwych kombinacji konfiguracyjnych . Tam, gdzie to konieczne, nadal istnieją części wymienne, ale bez potrzeby dużego formalizmu definiowania stałych interfejsów, ze względu na elastyczność pisania kaczego i względną prostotę języka.

Kylotan
źródło
4
To nie tyle ramy, co sam język. Aby stworzyć rodzaj elastyczności, jaką cieszą się języki pisma kaczego, języki o typie statycznym potrzebują bardzo wyrafinowanych ram i reguł. DI jest jedną z tych zasad. Pythonowie nie zastanawiają się dwa razy. Ludzie Java muszą naprawdę nad tym popracować.
S.Lott,
6
@ S.Lott - Całkowicie się z tobą zgadzam, z tym wyjątkiem, że ludzie w C ++ wydają się radzić sobie bez eksplozji wzorców projektowych i architektonicznych, pomimo pracy z podobnymi ograniczeniami jak Java. Wydaje mi się, że oznacza to różnicę kulturową, w której ludzie, mając do czynienia z 2 możliwymi sposobami zrobienia czegoś, wolą wyodrębnić inny interfejs w celu ułatwienia wzorca strategii, podczas gdy ludzie C ++ wkraczają od razu i dodają wartość bool i wyrażenie if ...
Kylotan,
3
@Finglas, więc jeśli mam kilkanaście klas, z których wszystkie używają mojej EmailSenderi zdecyduję się ją zastąpić DesktopNotifier, muszę ręcznie edytować 12 klas. Myślisz, że to prostsze i czystsze niż pisanie do INotifierinterfejsu i pozwalanie kontenerowi na wypracowanie szczegółów?
Podstawowy
1
Niestety pewien poziom złożoności jest rzeczywistością, z którą muszą zmierzyć się profesjonalni programiści. Widzę krytykę, ale nie ma rozwiązań w tej odpowiedzi. Jakie jest rozwiązanie „pytoniczne” dla tego problemu: Piszę bibliotekę i chcę zapewnić hak do logowania (coś w stylu PHP PSR-3 LoggerInterface). Wiem, jak korzystać z poziomów dziennika, ale nie obchodzi mnie, w jaki sposób program je zgłasza. Jaki jest czysty sposób, aby umożliwić aplikacji klienckiej wstrzyknięcie tych szczegółów implementacji. Uwaga: inne części aplikacji mogą mieć różne implementacje tego interfejsu.
Rob
2
Moje pytanie nie dotyczy tego, w jaki sposób korzystasz ze standardowej biblioteki rejestrowania, ani nie chodzi o tworzenie różnych instancji klasy programu rejestrującego. Moje pytanie brzmi: w jaki sposób konfigurujesz swoją aplikację, aby różne części aplikacji mogły korzystać z różnych implementacji i nie przejmować się tymi szczegółami (pod warunkiem, że wiedzą, jak korzystać z interfejsu). To bardzo realny problem, który DI rozwiązał dla wielu aplikacji PHP, nad którymi pracowałem. Szukam ekwiwalentu python. A sugerowanie „po prostu nie komplikuj swojej aplikacji” nie jest odpowiedzią, której szukam.
Rob
-5

W przeciwieństwie do silnie wpisanej natury w Javie. Zachowanie podczas pisania kaczkami w Pythonie ułatwia przenoszenie obiektów.

Programiści Java koncentrują się na tworzeniu struktury klas i relacji między obiektami, jednocześnie zachowując elastyczność. IoC jest niezwykle ważny dla osiągnięcia tego celu.

Programiści Python koncentrują się na wykonaniu pracy. Po prostu łączą zajęcia, kiedy tego potrzebują. Nie muszą się nawet martwić o rodzaj klasy. Tak długo, jak może kwakać, jest kaczką! Ta natura nie pozostawia miejsca na IoC.

Jason Ching
źródło
4
Nadal musisz znaleźć coś, co zaskakuje.
andho