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.
Odpowiedzi:
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
Mamy nazwy „łączenie razem” i „w czasie wykonywania”:
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:
źródło
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.
źródło
MyClassInstances
klasę dla każdej z nichMyClass
w Javie, która zawiera tylko statyczne, w pełni zainicjowane instancje. To byłoby okablowane: Dfrom 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ćUser
implementację bez dotykania frameworka.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
:Django Rest Framework mocno wykorzystuje DI:
Pozwól, że przypomnę ( źródło ):
źródło
IsAuthenticated
,ScopedRateThrottle
) są tworzone przez klasę. Nie są przekazywane do konstruktora.IsAuthenticated
iScopedRateThrottle
nie 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.IsAuthenticated
iScopedRateThrottle
są zależnościami; są wstrzykiwane doFooView
. Nie ma znaczenia, kiedy i jak to się robi. Python nie jest Javą, więc istnieją różne sposoby implementacji tego.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.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:
Później kod może następnie utworzyć interfejs bazy danych, pisząc:
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.
źródło
import psycopg2 as database_interface
. Umieść tę linię w innym miejscuinjections.py
.self.database_interface
), które krzyczą koniecznie.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
źródło
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.
źródło
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:
_
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 .
źródło
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.
źródło
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:
Jednak spójrz na https://github.com/noodleflake/pyioc to może być to, czego szukasz.
tj. w pyioc
źródło
other.py
linii 1 znajduje się automatyczne rozwiązywanie zależności, ale nie liczyłoby się to jako zastrzyk zależności.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:
źródło
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.
źródło
The typical Python application is much simpler, just a bunch of scripts, without such a complex architecture.
- całkiem założeniepytest osprzęt wszystkie oparte na DI ( źródło )
źródło
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]
źródło
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.
źródło
EmailSender
i zdecyduję się ją zastąpićDesktopNotifier
, muszę ręcznie edytować 12 klas. Myślisz, że to prostsze i czystsze niż pisanie doINotifier
interfejsu i pozwalanie kontenerowi na wypracowanie szczegółów?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.
źródło