In Java ist IoC / DI eine sehr verbreitete Praxis, die in Webanwendungen, fast allen verfügbaren Frameworks und Java EE häufig verwendet wird. Auf der anderen Seite gibt es auch viele große Python-Webanwendungen, aber neben Zope (von dem ich gehört habe, dass das Codieren wirklich schrecklich sein sollte) 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 für Python, beispielsweise Springpython . Aber keiner von ihnen scheint sich praktisch zu gewöhnen. Zumindest bin ich noch nie auf eine auf Django oder sqlalchemy + <insert your favorite wsgi toolkit here>
basierende Webanwendung gestoßen, die so etwas verwendet.
Meiner Meinung nach hat IoC vernünftige Vorteile und würde es beispielsweise leicht machen, das Django-Standardbenutzermodell zu ersetzen, aber die umfangreiche Verwendung von Schnittstellenklassen und IoC in Python sieht etwas seltsam und nicht »pythonisch« aus. Aber vielleicht hat jemand eine bessere Erklärung, warum IoC in Python nicht weit verbreitet ist.
Antworten:
Ich glaube nicht wirklich , dass DI / IoC ist , dass ungewöhnlich in Python. Was jedoch ungewöhnlich ist, sind DI / IoC- Frameworks / Container .
Denken Sie darüber nach: Was macht ein DI-Container? Es erlaubt Ihnen
Wir haben Namen für "Verkabelung zusammen" und "zur Laufzeit":
Ein DI-Container ist also nichts anderes als ein Interpreter für eine dynamische Skriptsprache. Lassen Sie mich das umformulieren: Ein typischer Java / .NET DI-Container ist nichts anderes als ein beschissener Interpreter für eine wirklich schlechte dynamische Skriptsprache mit hintern hässlicher, manchmal XML-basierter Syntax.
Warum sollten Sie beim Programmieren in Python eine hässliche, schlechte Skriptsprache verwenden, wenn Sie über eine schöne, brillante Skriptsprache verfügen? Eigentlich ist das eine allgemeinere Frage: Wenn Sie in so ziemlich jeder Sprache programmieren, warum sollten Sie eine hässliche, schlechte Skriptsprache verwenden, wenn Sie Jython und IronPython zur Verfügung haben?
Um es noch einmal zusammenzufassen: Die Praxis von DI / IoC ist in Python aus genau den gleichen Gründen genauso wichtig wie in Java. Die Implementierung von DI / IoC ist jedoch in die Sprache integriert und oft so leicht, dass sie vollständig verschwindet.
(Hier eine kurze Beschreibung für eine Analogie: In der Assembly ist ein Unterprogrammaufruf eine ziemlich wichtige Angelegenheit - Sie müssen Ihre lokalen Variablen und Register im Speicher speichern, Ihre Rücksprungadresse irgendwo speichern und den Anweisungszeiger auf das von Ihnen aufgerufene Unterprogramm ändern. Sorgen Sie dafür, dass es nach Abschluss des Vorgangs irgendwie in Ihr Unterprogramm zurückspringt, platzieren Sie die Argumente an einer Stelle, an der der Angerufene sie finden kann, und so weiter. IOW: In der Assembly ist "Unterprogrammaufruf" ein Entwurfsmuster, und bevor es Sprachen wie gab In Fortran, in das Unterprogrammaufrufe integriert waren, erstellten die Benutzer ihre eigenen "Unterprogramm-Frameworks". Würden Sie sagen, dass Unterprogrammaufrufe in Python "ungewöhnlich" sind, nur weil Sie keine Unterprogramm-Frameworks verwenden?)
BTW: für ein Beispiel , wie es aussieht DI zu ihrem logischen Ende zu nehmen, einen Blick auf Gilad Bracha ‚s Neusprech Programmiersprache und seine Schriften über das Thema:
quelle
Ein Teil davon ist die Funktionsweise des Modulsystems in Python. Sie können eine Art "Singleton" kostenlos erhalten, 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 erstelltes / ausgefülltes Objekt erhalten.
Dies steht im Gegensatz zu Java, wo Sie keine tatsächlichen Instanzen von Objekten importieren. Dies bedeutet, dass Sie sie immer selbst instanziieren müssen (oder einen IoC / DI-Ansatz verwenden müssen). Sie können den Aufwand verringern, alles selbst instanziieren zu müssen, indem Sie über statische Factory-Methoden (oder tatsächliche Factory-Klassen) verfügen. Dann entsteht jedoch immer noch der Ressourcenaufwand, jedes Mal neue zu erstellen.
quelle
MyClassInstances
jeweils eine Klasse definiert wirdMyClass
, die nur statische, vollständig initialisierte Instanzen enthält. Das wäre verkabelt: Dfrom framework.auth.user import User
es möglicherweise besser,User = lookup('UserImplentation', 'framework.auth.user.User')
innerhalb des Frameworks zu schreiben (der 2. Parameter ist möglicherweise ein Standardwert). Dann könnten Benutzer des Frameworks dieUser
Implementierung ersetzen / spezialisieren , ohne das Framework zu berühren.IoC und DI sind in ausgereiftem Python-Code sehr verbreitet. Dank der Ententypisierung benötigen Sie einfach kein Framework, um DI zu implementieren.
Das beste Beispiel ist, wie Sie eine Django-Anwendung einrichten, indem Sie
settings.py
:Das Django Rest Framework nutzt DI stark:
Lassen Sie mich daran erinnern ( Quelle ):
quelle
IsAuthenticated
,ScopedRateThrottle
) von der Klasse instanziiert werden. Sie werden nicht an den Konstruktor übergeben.IsAuthenticated
undScopedRateThrottle
sind keine Instanzen, dies sind Klassen. Sie werden instanziiert, wenn ein FooView erstellt wird (tatsächlich, wenn das FooView eine Anforderung verarbeitet). Auf jeden Fall ist dies nur ein Implementierungsdetail.IsAuthenticated
undScopedRateThrottle
sind die Abhängigkeiten; Sie werden in die injiziertFooView
. Es spielt keine Rolle, wann oder wie dies getan wird. Python ist kein Java, daher gibt es verschiedene Möglichkeiten, dies zu implementieren.renderer_classes = (JSONRenderer, BrowsableAPIRenderer, XMLRenderer)
. Das Verspotten ist so einfach wie@unittest.patch('myapp.views.FooView.permission_classes')
. Das verzweifelte Bedürfnis, "etwas zu übergeben", ist eine Folge der "Java-Vorgehensweise", da Java eine kompilierte und statisch typisierte Sprache ist, der es an starken Metaprogrammierfähigkeiten mangelt.Django macht großen Gebrauch von der Umkehrung der Kontrolle. Beispielsweise wird der Datenbankserver von der Konfigurationsdatei ausgewählt, und das Framework stellt Datenbankclients geeignete Datenbank-Wrapper-Instanzen bereit.
Der Unterschied ist, dass Python erstklassige Typen hat. Datentypen, einschließlich Klassen, sind selbst Objekte. Wenn Sie möchten, dass eine bestimmte Klasse verwendet wird, benennen Sie einfach die Klasse. Zum Beispiel:
Späterer Code kann dann eine Datenbankschnittstelle erstellen, indem er schreibt:
Anstelle der Boilerplate-Factory-Funktionen, die Java und C ++ benötigen, verwendet Python eine oder zwei Zeilen normalen Codes. Dies ist die Stärke der funktionalen gegenüber der imperativen Programmierung.
quelle
import psycopg2 as database_interface
. Setzen Sie diese Zeile in eininjections.py
et voilà.self.database_interface
), die unbedingt schreien müssen.Es scheint, dass die Leute wirklich nicht mehr verstehen, was Abhängigkeitsinjektion und Umkehrung der Kontrolle bedeuten.
Die Praxis der Inversion der Steuerung besteht darin, Klassen oder Funktionen zu haben, die von anderen Klassen oder Funktionen abhängen. Anstatt jedoch die Instanzen innerhalb der Klasse des Funktionscodes zu erstellen, ist es besser, sie als Parameter zu empfangen, damit eine lose Kopplung erreicht werden kann. Das hat viele Vorteile, da mehr Testbarkeit und das Liskov-Substitutionsprinzip erreicht werden können.
Sie sehen, durch die Arbeit mit Schnittstellen und Injektionen wird Ihr Code wartbarer, da Sie das Verhalten leicht ändern können, da Sie nicht eine einzelne Codezeile (möglicherweise ein oder zwei Zeilen in der DI-Konfiguration) Ihres Codes neu schreiben müssen Klasse, 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 entkoppelt und einfach zu warten zu halten, besteht darin, mindestens die Prinzipien der einzelnen Verantwortlichkeit, Substitution und Abhängigkeitsinversion zu befolgen.
Wofür eignet sich eine DI-Bibliothek, wenn Sie ein Objekt selbst in einem Paket instanziieren und importieren können, um es selbst zu injizieren? Die gewählte Antwort ist richtig, da Java keine prozeduralen Abschnitte (Code außerhalb von Klassen) hat, alles, was in langweilige Konfigurations-XMLs fließt, daher die Notwendigkeit einer Klasse, Abhängigkeiten von einer faulen Lademode zu instanziieren und einzufügen, damit Sie nicht wegblasen Ihre Leistung, während Sie auf Python nur die Injektionen in den Abschnitten "prozedural" (Code außerhalb von Klassen) Ihres Codes codieren
quelle
Ich habe Python seit einigen Jahren nicht mehr verwendet, aber ich würde sagen, dass es mehr damit zu tun hat, dass es eine dynamisch typisierte Sprache ist als alles andere. Zum Beispiel könnte ich in Java, wenn ich testen wollte, ob etwas entsprechend dem Standard geschrieben wurde, DI verwenden und einen beliebigen PrintStream übergeben, um den geschriebenen Text zu erfassen und zu überprüfen. Wenn ich jedoch in Ruby arbeite, kann ich die 'Puts'-Methode in STDOUT dynamisch ersetzen, um die Überprüfung durchzuführen, sodass DI vollständig aus dem Bild bleibt. Wenn der einzige Grund, warum ich eine Abstraktion erstelle, darin besteht, die Klasse zu testen, die sie verwendet (denken Sie an Dateisystemoperationen oder die Uhr in Java), führt DI / IoC zu unnötiger Komplexität der Lösung.
quelle
Eigentlich ist es ziemlich einfach, mit DI ausreichend sauberen und kompakten Code zu schreiben (ich frage mich, wird es dann pythonisch sein / bleiben , aber trotzdem :)), zum Beispiel bevorzuge ich tatsächlich diese Art der Codierung:
_
Ja, dies kann nur als eine einfache Form der Parametrisierung von Funktionen / Klassen angesehen werden, aber es macht seine Arbeit. Vielleicht reichen auch hier die standardmäßig mitgelieferten Python-Batterien aus.
PS Ich habe auch ein größeres Beispiel für diesen naiven Ansatz bei Dynamic Evaluating Simple Boolean Logic in Python veröffentlicht .
quelle
IoC / DI ist ein Designkonzept, aber leider wird es oft als ein Konzept angesehen, das für bestimmte Sprachen (oder Schreibsysteme) gilt. Ich würde gerne sehen, wie Abhängigkeitsinjektionscontainer in Python weitaus beliebter werden. Es gibt Spring, aber das ist ein Super-Framework und scheint ein direkter Port der Java-Konzepte zu sein, ohne viel Rücksicht auf "The Python Way" zu nehmen.
Angesichts der Anmerkungen in Python 3 entschied ich mich für einen Crack in einem voll funktionsfähigen, aber einfachen Abhängigkeitsinjektionscontainer: https://github.com/zsims/dic . Es basiert auf einigen Konzepten aus einem .NET-Abhängigkeitsinjektionscontainer (was IMO fantastisch ist, wenn Sie jemals in diesem Bereich spielen), ist jedoch mit Python-Konzepten mutiert.
quelle
Ich denke, aufgrund der Dynamik von Python sehen die Leute nicht oft die Notwendigkeit eines anderen dynamischen Frameworks. Wenn eine Klasse von dem neuen 'Objekt' erbt, können Sie dynamisch eine neue Variable erstellen ( https://wiki.python.org/moin/NewClassVsClassicClass ).
dh in einfacher Python:
Schauen Sie sich jedoch https://github.com/noodleflake/pyioc an, dies könnte das sein, wonach Sie suchen.
dh In pyioc
quelle
other.py
Zeile 1 gibt es eine automatisierte Abhängigkeitsauflösung, die jedoch nicht als Abhängigkeitsinjektion gewertet wird.Ich unterstütze die Antwort von "Jörg W Mittag": "Die Python-Implementierung von DI / IoC ist so leicht, dass sie vollständig verschwindet."
Um diese Aussage zu stützen , werfen Sie einen Blick auf das berühmte Beispiel von Martin Fowler, das von Java nach Python portiert wurde: Python: Design_Patterns: Inversion_of_Control
Wie Sie dem obigen Link entnehmen können, kann ein "Container" in Python in 8 Codezeilen geschrieben werden:
quelle
Meine 2 Cent sind, dass Sie es in den meisten Python-Anwendungen nicht brauchen und selbst wenn Sie es brauchen, besteht die Möglichkeit, dass viele Java-Hasser (und inkompetente Geiger, die glauben, Entwickler zu sein) es als etwas Schlechtes betrachten, nur weil es in Java beliebt ist .
Ein IoC-System ist tatsächlich nützlich, wenn Sie komplexe Netzwerke von Objekten haben, bei denen jedes Objekt eine Abhängigkeit für mehrere andere sein kann und wiederum selbst von anderen Objekten abhängig ist. In einem solchen Fall möchten Sie alle diese Objekte einmal definieren und über einen Mechanismus verfügen, mit dem sie automatisch zusammengesetzt werden können, basierend auf so vielen impliziten Regeln wie möglich. Wenn Sie auch eine Konfiguration haben, die vom Benutzer / Administrator der Anwendung auf einfache Weise definiert werden muss, ist dies ein weiterer Grund, sich ein IoC-System zu wünschen, das seine Komponenten aus einer einfachen XML-Datei lesen kann (dies wäre die Konfiguration).
Die typische Python-Anwendung ist viel einfacher, nur eine Reihe von Skripten, ohne eine so komplexe Architektur. Persönlich bin ich mir bewusst, was ein IoC tatsächlich ist (im Gegensatz zu denen, die hier bestimmte Antworten geschrieben haben), und ich habe in meiner begrenzten Python-Erfahrung nie das Bedürfnis danach gespürt (außerdem verwende ich Spring nicht überall, nicht bei den Vorteilen es gibt nicht rechtfertigen seinen Entwicklungsaufwand).
Es gibt jedoch Python-Situationen, in denen der IoC-Ansatz tatsächlich nützlich ist, und tatsächlich habe ich hier gelesen, dass Django ihn verwendet.
Dieselbe Argumentation könnte auf die aspektorientierte Programmierung in der Java-Welt angewendet werden, mit dem Unterschied, dass die Anzahl der Fälle, in denen sich AOP wirklich lohnt, noch geringer ist.
quelle
The typical Python application is much simpler, just a bunch of scripts, without such a complex architecture.
- eine ziemliche Annahmepytest fixtures alle basierend auf DI ( Quelle )
quelle
Ich stimme @Jorg darin zu, dass DI / IoC in Python möglich, einfacher und noch schöner ist. Was fehlt, sind die Frameworks, die es unterstützen, aber es gibt einige Ausnahmen. Um einige Beispiele zu nennen, die mir in den Sinn kommen:
Mit Django-Kommentaren können Sie Ihre eigene Kommentarklasse mit Ihrer benutzerdefinierten Logik und Ihren Formularen verknüpfen. [Mehr Info]
Mit Django können Sie ein benutzerdefiniertes Profilobjekt verwenden, um es an Ihr Benutzermodell anzuhängen. Dies ist nicht vollständig IoC, aber ein guter Ansatz. Persönlich möchte ich das Loch-Benutzermodell wie das Kommentar-Framework ersetzen. [Mehr Info]
quelle
Meiner Meinung nach sind Dinge wie die Abhängigkeitsinjektion Symptome eines starren und überkomplexen Rahmens. Wenn der Hauptteil des Codes viel zu gewichtig wird, um leicht geändert zu werden, müssen Sie kleine Teile davon auswählen, Schnittstellen für sie definieren und dann den Benutzern ermöglichen, das Verhalten über die Objekte zu ändern, die an diese Schnittstellen angeschlossen werden. Das ist alles schön und gut, aber es ist besser, diese Komplexität überhaupt zu vermeiden.
Es ist auch das Symptom einer statisch typisierten Sprache. Wenn das einzige Werkzeug, mit dem Sie Abstraktion ausdrücken müssen, die Vererbung ist, dann verwenden Sie das so ziemlich überall. Trotzdem ist C ++ ziemlich ähnlich, hat aber nie die Faszination für Builder und Schnittstellen aufgegriffen, die Java-Entwickler überall hatten. Es ist leicht, mit dem Traum, flexibel und erweiterbar zu sein, überschwänglich zu werden, wenn man viel zu viel generischen Code mit wenig wirklichem Nutzen schreibt . Ich denke, es ist eine kulturelle Sache.
Normalerweise denke ich, dass Python-Leute es gewohnt sind, das richtige Tool für den Job auszuwählen, das ein kohärentes und einfaches Ganzes ist, und nicht das One True Tool (mit tausend möglichen Plugins), das alles kann, außer eine verwirrende Reihe möglicher Konfigurationspermutationen bietet . Es gibt immer noch austauschbare Teile, wo dies erforderlich ist, aber aufgrund der Flexibilität der Ententypisierung und der relativen Einfachheit der Sprache ist der große Formalismus der Definition fester Schnittstellen nicht erforderlich.
quelle
EmailSender
und beschließen, sie durch eine zu ersetzenDesktopNotifier
, muss ich 12 Klassen von Hand bearbeiten. Und Sie denken, das ist einfacher und sauberer, als nur auf eineINotifier
Schnittstelle zu schreiben und den Container die Details herausarbeiten zu lassen?Im Gegensatz zu der stark typisierten Natur in Java. Das Enten-Tippverhalten von Python macht es so einfach, Objekte weiterzugeben.
Java-Entwickler konzentrieren sich auf die Konstruktion der Klassenstruktur und der Beziehung zwischen Objekten, während die Dinge flexibel bleiben. IoC ist dafür äußerst wichtig.
Python-Entwickler konzentrieren sich darauf, die Arbeit zu erledigen. Sie verkabeln nur Klassen, wenn sie es brauchen. Sie müssen sich nicht einmal um die Art der Klasse kümmern. Solange es quaken kann, ist es eine Ente! Diese Natur lässt keinen Raum für IoC.
quelle