439 Stimmen

Warum ist IoC / DI in Python nicht üblich?

In Java IoC / DI ist eine sehr verbreitete Praxis, die in Webanwendungen, fast allen verfügbaren Frameworks und Java EE ausgiebig genutzt wird. Auf der anderen Seite gibt es auch viele große Python-Webanwendungen, aber abgesehen von Zope (das, wie ich gehört habe, wirklich schrecklich zu programmieren sein soll) scheint IoC in der Python-Welt nicht sehr verbreitet zu sein. (Bitte nennen Sie einige Beispiele, wenn Sie denken, dass ich falsch liege).

Es gibt natürlich mehrere Klone beliebter Java-IoC-Frameworks, die für Python verfügbar sind, springpython zum Beispiel. Aber keiner von ihnen scheint praktisch genutzt zu werden. Zumindest bin ich noch nie auf ein solches Produkt gestoßen. Django o sqlalchemy + <insert your favorite wsgi toolkit here> basierte Webanwendung, die etwas Ähnliches verwendet.

Meiner Meinung nach hat IoC vernünftige Vorteile und würde es einfach machen, z.B. das django-default-user-model zu ersetzen, aber die extensive Nutzung von Schnittstellenklassen und IoC in Python sieht ein wenig seltsam und nicht "pythonisch" aus. Aber vielleicht hat jemand eine bessere Erklärung, warum IoC in Python nicht weit verbreitet ist.

213voto

Jörg W Mittag Punkte 349574

Ich glaube eigentlich nicht, dass DI/IoC dass ungewöhnlich in Python. Was ist Ungewöhnlich sind jedoch DI/IoC Rahmenwerke/Container .

Überlegen Sie einmal: Was macht ein DI-Container? Er ermöglicht es Ihnen

  1. unabhängige Komponenten zu einer vollständigen Anwendung zusammenfügen ...
  2. ... zur Laufzeit.

Wir haben Bezeichnungen für "zusammen verdrahten" und "zur Laufzeit":

  1. Skripting
  2. dynamisch

Ein DI-Container ist also nichts anderes als ein Interpreter für eine dynamische Skriptsprache. Lassen Sie es mich anders ausdrücken: Ein typischer Java/.NET-DI-Container ist nichts anderes als ein beschissener Interpreter für eine wirklich schlechte dynamische Skriptsprache mit einer potthässlichen, manchmal XML-basierten Syntax.

Wenn Sie in Python programmieren, warum sollten Sie eine hässliche, schlechte Skriptsprache verwenden wollen, wenn Sie eine schöne, brillante Skriptsprache zur Verfügung haben? Eigentlich ist das eine allgemeinere Frage: Wenn man in so ziemlich jeder Sprache programmiert, warum sollte man dann eine hässliche, schlechte Skriptsprache verwenden, wenn man Jython und IronPython zur Verfügung hat?

Um es noch einmal zusammenzufassen: Die Praxis von DI/IoC ist in Python genauso wichtig wie in Java, und zwar aus genau denselben Gründen. Die Umsetzung von DI/IoC ist jedoch in die Sprache eingebaut und oft so leichtgewichtig, dass sie völlig verschwindet.

(Hier ein kurzer Hinweis als Analogie: In Assembler ist ein Unterprogrammaufruf eine ziemlich große Sache - Sie müssen Ihre lokalen Variablen und Register im Speicher speichern, Ihre Rücksprungadresse irgendwo speichern, den Befehlszeiger auf das Unterprogramm, das Sie aufrufen, ändern, dafür sorgen, dass es irgendwie in Ihr Unterprogramm zurückspringt, wenn es fertig ist, die Argumente irgendwo ablegen, wo der Aufrufende sie finden kann, und so weiter. IOW: in Assembly ist der "Unterprogrammaufruf" ein Design Pattern, und bevor es Sprachen wie Fortran gab, die Unterprogrammaufrufe eingebaut hatten, haben die Leute ihre eigenen "Unterprogramm-Frameworks" gebaut. Würden Sie sagen, dass Unterprogrammaufrufe in Python "unüblich" sind, nur weil Sie keine Unterprogramm-Frameworks verwenden?)

Nebenbei bemerkt: Ein Beispiel dafür, wie es aussieht, wenn man DI zu seiner logischen Schlussfolgerung bringt, finden Sie unter Gilad Bracha 's Newspeak Programmiersprache und seine Schriften zu diesem Thema:

91voto

Max Malysh Punkte 25480

IoC und DI sind in ausgereiftem Python-Code sehr verbreitet. Dank Duck Typing braucht man kein Framework, um DI zu implementieren.

Das beste Beispiel ist, wie Sie eine Django-Anwendung mit 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',
    }
}

Das Django Rest Framework nutzt DI in hohem Maße:

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

Lassen Sie mich daran erinnern ( Quelle ):

"Dependency Injection" ist ein 25-Dollar-Begriff für ein 5-Cent-Konzept. [...] Dependency Injection bedeutet, einem Objekt seine Instanzvariablen zu geben. [...].

72voto

TM. Punkte 101846

Ein Teil davon ist die Art und Weise, wie das Modulsystem in Python funktioniert. Sie können eine Art "Singleton" umsonst bekommen, indem Sie es einfach aus einem Modul importieren. Definieren Sie eine tatsächliche Instanz eines Objekts in einem Modul, und dann kann jeder Client-Code es importieren und tatsächlich ein funktionierendes, vollständig konstruiertes / bestücktes Objekt erhalten.

Dies steht im Gegensatz zu Java, wo Sie keine tatsächlichen Instanzen von Objekten importieren. Das bedeutet, dass Sie sie immer selbst instanziieren müssen (oder eine Art von IoC/DI-Ansatz verwenden). Sie können den Aufwand, alles selbst instanziieren zu müssen, durch statische Factory-Methoden (oder tatsächliche Factory-Klassen) abmildern, aber dann entsteht immer noch der Ressourcen-Overhead, jedes Mal neue Instanzen zu erstellen.

38voto

jhonatan teixeira Punkte 720

Es scheint, dass die Leute wirklich nicht mehr verstehen, was Dependency Injection und Inversion of Control bedeuten.

Die Praxis der Kontrollumkehr besteht darin, Klassen oder Funktionen zu haben, die von anderen Klassen oder Funktionen abhängen, aber anstatt die Instanzen innerhalb des Klassen- oder Funktionscodes zu erstellen, ist es besser, sie als Parameter zu erhalten, so dass eine lose Kopplung erreicht werden kann. Das hat viele Vorteile, wie z.B. eine bessere Testbarkeit und das Erreichen des Liskov-Substitutionsprinzips.

Sie sehen, wenn Sie mit Schnittstellen und Injektionen arbeiten, wird Ihr Code wartbarer, da Sie das Verhalten leicht ändern können, weil Sie keine einzige Zeile Code (vielleicht ein oder zwei Zeilen in der DI-Konfiguration) Ihrer Klasse neu schreiben müssen, um ihr Verhalten zu ändern, da die Klassen, die die Schnittstelle implementieren, auf die Ihre Klasse wartet, unabhängig voneinander variieren können, solange sie der Schnittstelle folgen. Eine der besten Strategien, um Code zu entkoppeln und einfach zu warten, ist es, zumindest die Prinzipien der einzigen Verantwortung, der Substitution und der Umkehrung von Abhängigkeiten zu befolgen.

Wozu ist eine DI-Bibliothek gut, wenn man ein Objekt selbst innerhalb eines Pakets instanziieren und es importieren kann, um es selbst zu injizieren? Die gewählte Antwort ist richtig, da Java keine prozeduralen Abschnitte hat (Code außerhalb von Klassen), alles, was in langweilige Konfigurations-xml's geht, daher die Notwendigkeit einer Klasse zu instanziieren und injizieren Abhängigkeiten auf eine Lazy-Load-Mode, so dass Sie nicht wegblasen Ihre Leistung, während auf Python Sie nur Code die Injektionen in den "prozeduralen" (Code außerhalb von Klassen) Abschnitte Ihres Codes.

38voto

Daniel Newby Punkte 2304

Django macht großen Gebrauch von der Umkehrung der Kontrolle. So wird beispielsweise der Datenbankserver über die Konfigurationsdatei ausgewählt, und das Framework stellt dann den Datenbank-Clients geeignete Datenbank-Wrapper-Instanzen zur Verfügung.

Der Unterschied ist, dass Python Typen erster Klasse hat. Datentypen, einschließlich Klassen, sind selbst Objekte. Wenn Sie möchten, dass etwas eine bestimmte Klasse verwendet, benennen Sie einfach die Klasse. Zum Beispiel:

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

Späterer Code kann dann durch Schreiben eine Datenbankschnittstelle erstellen:

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

Anstelle der Boilerplate-Factory-Funktionen, die Java und C++ benötigen, erledigt Python dies mit einer oder zwei Zeilen gewöhnlichem Code. Dies ist die Stärke der funktionalen gegenüber der imperativen Programmierung.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X