Waarom is IoC/DI niet gebruikelijk in Python?

In Java IoC/ DIis een veel voorkomende praktijk die veel wordt gebruikt in webapplicaties, bijna alle beschikbare frameworks en Java EE. Aan de andere kant zijn er ook veel grote Python-webapplicaties, maar naast Zope (waarvan ik heb gehoord dat het echt verschrikkelijk zou moeten zijn om te coderen) lijkt IoC niet erg gebruikelijk in de Python-wereld. (Noem enkele voorbeelden als je denkt dat ik het mis heb).

Er zijn natuurlijk verschillende klonen van populaire Java IoC-frameworks beschikbaar voor Python, bijvoorbeeld springpython. Maar geen van hen lijkt praktisch te worden gebruikt. Ik ben in ieder geval nog nooit een Djangoof sqlalchemy+<insert your favorite wsgi toolkit here>gebaseerde webtoepassing die zoiets gebruikt.

Naar mijn mening heeft IoC redelijke voordelen en zou het bijvoorbeeld gemakkelijk zijn om het django-default-user-model te vervangen, maar uitgebreid gebruik van interfaceklassen en IoC in Python ziet er een beetje vreemd uit en niet »pythonic«. Maar misschien heeft iemand een betere verklaring waarom IoC niet veel wordt gebruikt in Python.


Antwoord 1, autoriteit 100%

Ik denk niet dat DI/IoC datongebruikelijk is in Python. Wat echter isongebruikelijk, zijn DI/IoC frameworks/containers.

Denk er eens over na: wat doet een DI-container? Hiermee kunt u

  1. onafhankelijke componenten samenvoegen tot een complete applicatie …
  2. … tijdens runtime.

We hebben namen voor “samen bedraden” en “at runtime”:

  1. scripting
  2. dynamisch

Een DI-container is dus niets anders dan een tolk voor een dynamische scripttaal. Laat me dat eigenlijk anders formuleren: een typische Java/.NET DI-container is niets anders dan een waardeloze interpreter voor een echt slechte dynamische scripttaal met een lelijke, soms op XML gebaseerde syntaxis.

Als je programmeert in Python, waarom zou je dan een lelijke, slechte scripttaal willen gebruiken als je een mooie, briljante scripttaal tot je beschikking hebt? Eigenlijk is dat een meer algemene vraag: als je in vrijwel elke taal programmeert, waarom zou je dan een lelijke, slechte scripttaal willen gebruiken als je Jython en IronPython tot je beschikking hebt?

Dus, om het samen te vatten: de praktijkvan DI/IoC is net zo belangrijk in Python als in Java, om precies dezelfde redenen. De implementatievan DI/IoC is echter ingebouwd in de taal en vaak zo licht van gewicht dat het volledig verdwijnt.

(Hier is een korte kanttekening voor een analogie: in assembly is een subroutine-aanroep een behoorlijk grote deal – u moet uw lokale variabelen en registers in het geheugen opslaan, uw retouradres ergens opslaan, de instructieaanwijzer wijzigen in de subroutine die u aanroepen, zorg ervoor dat het op de een of andere manier terugspringt in uw subroutine wanneer het klaar is, plaats de argumenten ergens waar de aangeroepene ze kan vinden, enz. IOW: in assembly is “subroutine-aanroep” een ontwerppatroon, en daarvoor Waren talen zoals Fortran waarin subroutine-aanroepen waren ingebouwd, mensen hun eigen “subroutine-frameworks” aan het bouwen waren. Zou je zeggen dat subroutine-aanroepen “ongewoon” zijn in Python, alleen omdat je geen subroutine-frameworks gebruikt?)

BTW: voor een voorbeeld van hoe het eruit ziet om DI tot zijn logische conclusie te brengen, kijk eens op Gilad Bracha‘s Newspeak programmeertaalen zijn geschriften over dit onderwerp:


Antwoord 2, autoriteit 34%

IoC en DI komen veel voor in volwassen Python-code. Je hebt gewoon geen raamwerk nodig om DI te implementeren dankzij duck typing.

Het beste voorbeeld is hoe u een Django-toepassing instelt met behulp van 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 maakt intensief gebruik van 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

Laat me eraan herinneren (bron):

‘Dependency Injection’ is een term van 25 dollar voor een concept van 5 cent. […] Afhankelijkheidsinjectie betekent dat een object zijn instantievariabelen wordt gegeven. […].


Antwoord 3, autoriteit 29%

Een deel ervan is de manier waarop het modulesysteem in Python werkt. Je kunt een soort “singleton” gratis krijgen, gewoon door het uit een module te importeren. Definieer een echt exemplaar van een object in een module, en dan kan elke clientcode het importeren en daadwerkelijk een werkend, volledig geconstrueerd / gevuld object krijgen.

Dit in tegenstelling tot Java, waar je geen echte instanties van objecten importeert. Dit betekent dat je ze altijd zelf moet instantiëren (of een soort IoC/DI-stijlbenadering moet gebruiken). Je kunt het gedoe om alles zelf te instantiëren verminderen door statische fabrieksmethoden (of daadwerkelijke fabrieksklassen) te gebruiken, maar dan heb je nog steeds de bronoverhead van het daadwerkelijk maken van nieuwe elke keer.


Antwoord 4, autoriteit 17%

Django maakt veel gebruik van inversie van controle. De databaseserver wordt bijvoorbeeld geselecteerd door het configuratiebestand, waarna het framework de juiste database-wrapperinstanties biedt aan databaseclients.

Het verschil is dat Python eersteklas typen heeft. Gegevenstypen, inclusief klassen, zijn zelf objecten. Als je wilt dat iets een bepaalde klasse gebruikt, geef je de klasse een naam. Bijvoorbeeld:

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

Latere code kan dan een database-interface maken door te schrijven:

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

In plaats van de standaard fabrieksfuncties die Java en C++ nodig hebben, doet Python het met een of twee regels gewone code. Dit is de kracht van functioneel versus imperatief programmeren.


Antwoord 5, autoriteit 13%

Het lijkt erop dat mensen echt niet meer begrijpen wat Dependency Injection en inversion of control betekenen.

De praktijk van het gebruik van inversie van controle is om klassen of functies te hebben die afhankelijk zijn van andere klassen of functies, maar in plaats van de instanties binnen de klasse- of functiecode te maken, is het beter om ze als parameters te ontvangen, zodat losse koppeling kan worden bereikt. Dat heeft veel voordelen, zoals meer testbaarheid en het bereiken van het liskov-substitutieprincipe.

U ziet, door met interfaces en injecties te werken, uw code wordt meer onderhoudbaar, omdat u het gedrag gemakkelijk kunt wijzigen, omdat u geen enkele regel code (misschien een regel of twee op de DI-configuratie kunt herschrijven ) van uw klasse om het gedrag te wijzigen, aangezien de klassen die de interface implementeren die uw klasse wachten op, kan onafhankelijk variëren zolang ze de interface volgen. Een van de beste strategieën om de niet-afkoppeling van de code te behouden en gemakkelijk te onderhouden is ten minste de enige verantwoordelijkheid, substitutie- en afhankelijkheidsuitkeringsprincipes te volgen.

Waar is een di-bibliotheek goed voor als je een object zelf in een pakket kunt geven en importeert om het zelf te injecteren? Het gekozen antwoord is goed, aangezien Java geen procedurele secties heeft (code buiten de lessen), alles wat in saai configuratie XML’s gaat, vandaar de behoefte van een klasse om afhankelijkheden op een luie laadmode te injecteren en te injecteren, zodat u niet wegblaast Uw prestaties, terwijl u op Python alleen de injecties codeert in de secties “Procedural” (Code buiten de lessen) van uw code.


6, Autoriteit 6%

Hebben Python in verschillende jaren niet gebruikt, maar ik zou zeggen dat het meer te maken heeft met het een dynamisch getypte taal dan wat dan ook. Voor een eenvoudig voorbeeld, in Java, als ik wilde testen dat iets schreef om op de juiste manier een standaard uit te voeren, zou ik DI kunnen gebruiken en in elke printstream moet passeren om de tekst die wordt geschreven en verifieert het te verifiëren. Wanneer ik in robijn werk, kan ik echter dynamisch de ‘puts’-methode op stdout vervangen om de verifieer te doen, waardoor de foto volledig uit de foto wordt achtergelaten. Als de enige reden dat ik een abstractie maak, is het testen van de klas die het gebruikt (denk aan bestandssysteembewerkingen of de klok in Java), creëert Di / IOC onnodige complexiteit in de oplossing.


7, Autoriteit 5%

IOC / DI is een ontwerpconcept, maar helaas wordt het vaak genomen als een concept dat van toepassing is op bepaalde talen (of typesystemen). Ik zou graag zien dat de afhankelijkheid injectiecontainers veel populairder wordt in Python. Er is spring, maar dat is een super-raamwerk en lijkt een directe haven van de Java-concepten te zijn zonder veel aandacht voor “de Python Way”.

Gezien annotaties in Python 3, besloot ik om een ​​crack te hebben op een volledige gaten, maar eenvoudige, afhankelijkheid injectiecontainer: https://github.com/zsims/dic . Het is gebaseerd op sommige concepten van een .NET-afhankelijkheid injectiecontainer (die IMO fantastisch is als u ooit in die ruimte speelt), maar gemuteerd met Python-concepten.


8, Autoriteit 3%

Pytest-armaturen allemaal gebaseerd op di (bron )


9

Naar mijn mening zijn dingen als afhankelijkheidsinjectie symptomen van een stijve en over complexe kader. Wanneer het hoofdgedeelte van de code veel te zwaar wordt om gemakkelijk te veranderen, merkt u dat u kleine delen ervan hoeft te kiezen, de interfaces voor hen te definiëren en vervolgens het gedrag te wijzigen via de objecten die in die interfaces aansluiten. Dat is alles goed en goed, maar het is beter om dat soort complexiteit in de eerste plaats te vermijden.

Het is ook het symptoom van een statisch getypte taal. Wanneer de enige tool die u moet uiten, is abstractie erfenis, dan is dat vrijwel wat u overal gebruikt. Dat gezegd hebbende, is C++ vrij vergelijkbaar, maar nooit de fascinatie met bouwers en interfaces opgehaald, overal die Java-ontwikkelaars deden. Het is gemakkelijk om over-uitbundig te worden met de droom om flexibel en uitbreidbaar te zijn tegen de kosten van het schrijven Veel te veel generieke code met weinig echt voordeel . Ik denk dat het een cultureel ding is.

Meestal denk ik dat Python-mensen worden gebruikt om de juiste tool voor de klus te kiezen, wat een coherent en eenvoudig geheel is, in plaats van het ene echte tool (met duizend mogelijke plug-ins) die alles kan doen dan een verbijsterende array van Mogelijke configuratie-permutaties. Er zijn nog steeds uitwisselbare onderdelen waar nodig, maar zonder dat het grote formalisme van het definiëren van vaste interfaces, vanwege de flexibiliteit van eend-typen en de relatieve eenvoud van de taal.


10

In tegenstelling tot de sterke getypte natuur in Java. Python’s Eend Typing-gedrag maakt het zo gemakkelijk om objecten rond te halen.

Java-ontwikkelaars concentreren zich op de constructie van de klassenstress en relatie tussen objecten, terwijl het flexibel houdt. IOC is uitermate belangrijk om dit te bereiken.

Python-ontwikkelaars focussen op het krijgen van het werk gedaan. Ze geven gewoon klassen op wanneer ze het nodig hebben. Ze hoeven zich niet eens zorgen te maken over het type van de klasse. Zolang het kan kweken, is het een eend! Deze natuur laat geen ruimte voor IOC.

Other episodes