Warum ist IoC / DI in Python nicht üblich?

313

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.

tux21b
quelle
2
Meine Vermutung, der gleiche Grund, warum es in Ruby, eingebauten Mixins und offenen Klassen weniger beliebt ist
Sam Saffron
3
Hast du jemals Springpython probiert? es funktioniert nicht einmal wie angekündigt. zumindest im aop Teil. Alles andere dort ist nicht sehr nützlich, es sei denn, Sie kommen aus Java und benötigen während des Übergangs ein gewisses Maß an Komfort.
Tom Willis
6
Bitte achten Sie darauf, zwischen der Verwendung von DI und der Verwendung eines IOC-Frameworks zu unterscheiden. Ersteres ist ein Entwurfsmuster, letzteres ist ein Rahmen, der die automatisierte Verwendung des ersteren unterstützt.
Doug
Doug, ich glaube, Sie wollten damit sagen, dass DI das kreative Merkmal ist, das mit dem Decorator-Muster erzielt wird.
Njappboy
4
Ich würde gerne eine Antwort sehen, die sich mit den Problemen befasst, die DI in der realen Welt löst: Lebenszeitmanagement, einfaches Stubbing von Tests usw. Wenn es einen pythonischeren Weg gibt, diese zu lösen, bin ich ganz Ohr.
Josh Noe

Antworten:

198

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

  1. Verdrahten Sie unabhängige Komponenten zu einer vollständigen Anwendung ...
  2. ... zur Laufzeit.

Wir haben Namen für "Verkabelung zusammen" und "zur Laufzeit":

  1. Skripterstellung
  2. dynamisch

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:

Jörg W Mittag
quelle
58
Während ich zustimme. Der XML-Kommentar ist falsch. Viele (zumindest die modernen) IOC-Container verwenden Konvention (Code) über Konfiguration (XML).
Finglas
20
Nichts hindert Sie daran, die Verkabelung explizit in Java zu schreiben, aber da Sie immer mehr Dienste haben, werden Abhängigkeiten komplexer. Ein DI-Container ist wie Make: Sie deklarieren die Abhängigkeiten und der Container initialisiert sie in der richtigen Reihenfolge. Guice ist ein Java DI-Framework, in dem alles in Java-Code geschrieben ist. Durch deklaratives Schreiben bietet ein DI-Container auch Unterstützung für die Nachbearbeitung der Deklerationen vor der Initialisierung (z. B. Ersetzen von Eigenschaftsplatzhaltern durch tatsächliche Werte)
IttayD
133
"Die Implementierung von DI / IoC ist jedoch in die Sprache integriert und oft so leicht, dass sie vollständig verschwindet." Abwahl, weil dies kategorisch falsch ist. DI ist ein Muster, bei dem eine Schnittstelle an den Konstruktor übergeben wird. Es ist nicht in Python integriert.
Doug
146
Downvote, Verkabelung zusammen hat nichts mit Scripting zu tun, DI ist ein Muster, und es ist nicht gleichbedeutend mit Scripting
Luxspes
38
Ich bin damit nicht einverstanden. DI behebt nicht das Fehlen dynamischer Skripte in statischen Sprachen. Es bietet ein Framework zum Konfigurieren und Zusammenstellen der Teile Ihrer Anwendung. Ich hörte einmal einen Ruby-Entwickler sagen, dass DI in dynamischen Sprachen nicht notwendig ist. Aber er hat Rails verwendet ... Rails ist nur eine Art großer DI-Container, der mithilfe von Konventionen herausfindet, welche Teile wann konfiguriert werden müssen. Er brauchte DI nicht, weil Rails das Problem löste, die Teile für ihn zu finden.
Brian Genisio
51

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.

TM.
quelle
2
Das macht Sinn. Wenn ich eine Implementierung in Python ändern möchte, importiere ich einfach von einem anderen Speicherort mit demselben Namen. Aber jetzt denke ich, ob es auch umgekehrt möglich ist, indem in Java MyClassInstancesjeweils eine Klasse definiert wird MyClass, die nur statische, vollständig initialisierte Instanzen enthält. Das wäre verkabelt: D
tux21b
2
Und noch eine Idee: Wenn Sie eine Möglichkeit zum Ändern solcher Importe in Python bereitstellen, können Sie Implementierungen problemlos ersetzen, ohne alle Python-Dateien zu berühren. Stattdessen ist from 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 die UserImplementierung ersetzen / spezialisieren , ohne das Framework zu berühren.
Tux21b
14
Wenn Sie es vereinfachen, antworten Sie, im wirklichen Leben brauchen Sie selten nur "einen Singleton", Sie müssen den Bereich steuern (Sie benötigen möglicherweise einen lokalen Thread-Singleton oder einen Sitzungs-Singleton usw.), dies lässt mich denken, dass die Art der Probleme In Python gelöst sind nicht die Art von realen Problemen, die tatsächlich in einem Unternehmen gelöst werden
Luxspes
3
Tatsächlich geht es bei DI darum, Abhängigkeiten von Code testen und entkoppeln zu können. Die Importfunktion ähnelt auch statischen Importen in Java, mit denen ich eine einzelne Instanz eines Objekts importieren kann.
Richard Warburton
1
"Sie können eine Art" Singleton "kostenlos erhalten, indem Sie es einfach aus einem Modul importieren." Dies kann in Java einfach durchgeführt werden, indem ein statisches Instanzfeld deklariert und auf einen Wert gesetzt wird. Dies ist kein Sol
ggranum
45

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:

# 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 stark:

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. [...] Abhängigkeitsinjektion bedeutet, einem Objekt seine Instanzvariablen zu geben. [...].

Max Malysh
quelle
8
+1. Gut ausgedrückt. Als Python-Programmierer war ich von einer ganzen Interviewpräsentation über DI-Frameworks in C # völlig verblüfft. Es hat eine Weile gedauert, bis mir klar wurde, dass ich es in Flask-Apps bereits die ganze Zeit getan habe, ohne darüber nachzudenken, weil Sie kein Framework benötigen. Für jemanden, der nichts anderes als C # / Java weiß, ist die Frage sinnvoll. Für Enten-typisierte Sprachprogrammierer ist es nur natürlich und wie Sie sagen, "25-Dollar-Begriff für ein 5-Cent-Konzept".
Samuel Harmer
5
err ... dies ist keine Abhängigkeitsinjektion, da die Instanzen ( IsAuthenticated, ScopedRateThrottle) von der Klasse instanziiert werden. Sie werden nicht an den Konstruktor übergeben.
Dopatraman
5
IsAuthenticatedund ScopedRateThrottlesind 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. IsAuthenticatedund ScopedRateThrottlesind die Abhängigkeiten; Sie werden in die injiziert FooView. Es spielt keine Rolle, wann oder wie dies getan wird. Python ist kein Java, daher gibt es verschiedene Möglichkeiten, dies zu implementieren.
Max Malysh
3
@ MaxMalysh Ich stimme Dopatraman in diesem Punkt zu. Dies ist nicht einmal IoC, da die Klasse selbst "fest codierte" Abhängigkeiten zu einer bestimmten Klasse aufweist. In IoC sollte die Abhängigkeit anstelle von fest codiert bereitgestellt werden. Darüber hinaus haben Sie in Dependency Injection eine Entität, die für die Verwaltung der Lebenszyklen jedes Dienstes verantwortlich ist und diese in diesem Fall injiziert. Die Lösung wurde in keiner dieser Lösungen bereitgestellt.
Ricardo Alves
3
@alex Nein, Sie müssen Ihren Code nicht ändern, um einen anderen Renderer zu verwenden. Sie können sogar mehrere Renderer gleichzeitig verwenden : 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.
Max Malysh
35

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:

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

Späterer Code kann dann eine Datenbankschnittstelle erstellen, indem er schreibt:

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

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.

Daniel Newby
quelle
4
Was Sie Code nennen, ist eigentlich der Verkabelungsteil. Das wäre das XML Ihres ioc-Frameworks. Es könnte eigentlich einfach so geschrieben werden import psycopg2 as database_interface. Setzen Sie diese Zeile in ein injections.pyet voilà.
Spektren
29
Ähm. Was du dort machst, ist so ziemlich das Lehrbuch, das Daniel braucht.
Shayne
Es ist definitiv ein zwingender Code, aber es ist irgendwie funktional, weil es einen aufrufbaren Wert verwendet.
Jeremy
5
Ist das nicht nur eine erstklassige Funktion? en.wikipedia.org/wiki/First-class_function Nur weil Sie sie haben und verwenden, wird Ihr Code nicht funktionsfähig. Hier treten einige Nebenwirkungen auf (z. B. Änderungen self.database_interface), die unbedingt schreien müssen.
hjc1710
15

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

jhonatan teixeira
quelle
Sie vermissen immer noch, dass ein IoC / DI die Objekte automatisch miteinander verbindet. Es ist nicht viel möglich, dies zur Laufzeit zu tun (Java kann dies sowieso durch Reflektion tun), es ist so, dass das Framework sich darum kümmert und Sie es nicht explizit tun müssen. Es ist auch irrelevant, prozedurale Abschnitte zu haben. Nichts hindert einen daran, eine vollständig prozedurale App in Java zu schreiben, indem Klassen als bloße Container statischer Unterprogramme und Funktionen verwendet werden, ohne überhaupt OOP-Funktionen zu verwenden.
Zakmck
@zakmck: Im Abschnitt "prozedural" von Python geht es hier nicht wirklich darum, prozeduralen Code zu schreiben. Was den "prozeduralen" Abschnitt von Python von statischen Sprachen unterscheidet, ist die Möglichkeit, prozeduralen Code in einen Klassenkörper einzufügen, der während der Klassendefinitionszeit ausgeführt wird, Importanweisungen in if-Anweisungen einzufügen und eine Klassenfactory einfach durch Definieren von Klassen zu erstellen innerhalb einer Fabrikmethode. Dies sind Dinge, die Sie in statischen Sprachen nicht wirklich tun können und die die meisten Probleme lösen, die IOC / DI zu lösen versucht hat. Die Metaprogrammierung in Python sieht oft genauso aus wie normaler Python-Code.
Lie Ryan
@LieRyan, Sie können dies mit Reflection tun oder, wenn Sie es häufig oder zur Laufzeit benötigen, die statische Sprache aus einer anderen Sprache wie Groovy (die für das einfache Spielen mit Java entwickelt wurde) oder sogar Python selbst aufrufen. Dies hat jedoch wenig mit IoC / DI-Frameworks zu tun. Ihr Zweck besteht darin, den größten Teil der prozeduralen Objektverdrahtung automatisch für Sie zu erledigen und dabei nur auf Definitionen zurückzugreifen. Leider verfehlen die meisten Antworten hiermit diesen Punkt.
Zakmck
12

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.

bcarlso
quelle
3
Es überrascht mich immer wieder, dass Leute, die bereit sind, die Funktionsweise eines Systems zu ändern, um zu testen, ob es funktioniert. Jetzt müssen Sie testen, ob Ihre Tests keine Nebenwirkungen verursachen.
Basic
2
Er spricht davon, die Puts-Methode nur im Testbereich zu ändern. Es ist wie eine Scheinmethode für injizierte Objekte.
dpa
2
@Basic, das ist in Unit-Tests ziemlich normal . Eigentlich ist es ratsam, dies in diesen Tests zu tun, da Sie Ihre Testfallabdeckung nicht mit mehr als einem Codeblock (dem zu testenden) verschmutzen möchten. Es wäre jedoch falsch, dies für Integrationstests zu tun. Vielleicht ist es das, worauf Sie sich in Ihrem Kommentar beziehen?
Samuel Grigolato
1
Testbarkeit ist für mich ein erstklassiges Anliegen. Wenn ein Design nicht testbar ist, ist es kein gutes Design, und ich habe kein Problem damit, das Design zu ändern, um es testbarer zu machen. Ich muss erneut bestätigen, dass es immer noch funktioniert, aber das ist in Ordnung. Testbarkeit ist ein durchaus gültiger Grund, den Code IMO zu ändern
Carlos Rodriguez
10

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:

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!

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 .

mlvljr
quelle
3
Für einfache Fälle, die funktionieren könnten, stellen Sie sich einfach einen einfachen Web-Blog-Controller vor, der verschiedene Modelle verwendet (Post, Kommentar, Benutzer). Wenn Sie möchten, dass der Benutzer sein eigenes Post-Modell (mit einem zusätzlichen Viewcount-Attribut, um dies zu verfolgen) und sein eigenes Benutzermodell mit mehr Profilinformationen usw. einfügt, sehen alle Parameter möglicherweise verwirrend aus. Darüber hinaus möchte der Benutzer möglicherweise auch das Request-Objekt ändern, um eine Dateisystemsitzung anstelle einer einfachen Cookie-basierten Sitzung oder Ähnlichem zu unterstützen. Sie werden also in Kürze viele Parameter erhalten.
Tux21b
1
@ tux21b Nun, es gibt "wesentliche Komplexität", die die Benutzer implementieren möchten, es gibt architektonische Lösungen dafür (von denen einige in Bezug auf Entwicklung und möglicherweise Wartungszeit, Ausführungsgeschwindigkeit usw. nicht schlechter sind als die anderen . ), und es gibt menschliche Fähigkeiten, die API- und Softwarearchitektur zu verstehen. Wenn es überhaupt keine vom Menschen verständliche Lösung gibt (nicht nur unter denen, die (irgendeine Form von) DI verwenden) ... nun, wer hat gesagt, dass alle Probleme lösbar sind? Und viele standardmäßig zugewiesene (aber nach Wahl des Benutzers austauschbare) Parameter können tatsächlich oft ausreichen.
mlvljr
9

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.

zsims
quelle
6

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:

#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()

Schauen Sie sich jedoch https://github.com/noodleflake/pyioc an, dies könnte das sein, wonach Sie suchen.

dh In 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
quelle
2
Die Tatsache, dass beide Versionen dieselbe Menge an Code verwenden, erklärt in hohem Maße, warum die Verwendung eines Frameworks nicht sehr beliebt ist.
Spektren
In other.pyZeile 1 gibt es eine automatisierte Abhängigkeitsauflösung, die jedoch nicht als Abhängigkeitsinjektion gewertet wird.
Andho
Service Locators sind normalerweise ein Anti-Pattern, nur um es zu sagen.
PmanAce
6

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:

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
quelle
42
Dies bleibt selbst bei den schwächsten DI-Behältern weit hinter den Erwartungen zurück. Wo ist die Lebensdauerverwaltung, die Auflösung rekursiver Abhängigkeiten, die Fähigkeit zum Verspotten oder - falls dies nicht der Fall ist - die Konfiguration? Dies ist nichts weiter als eine Typensuche und ein Cache, die nicht mit IoC identisch sind.
Basic
2
Vor Jahren schrieb ich ein kleines DI-Framework mit Metaklassen als Übung. Das Ganze ist eine einzelne Datei mit null Importen und Doctests, die es selbsterklärend machen. Es zeigt, dass grundlegende Funktionen nicht so schwer auf eine Weise zu implementieren sind, die sogar "pythonisch" ist, aber ich finde es aufrichtig traurig, dass keine vollständige Lösung so stark an Bedeutung gewonnen hat wie Spring in Java und jeder benutzerdefinierte Plugin-Architekturen erstellt.
Andrea Ratto
2

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.

zakmck
quelle
Gibt es eine Referenz-URL zur Informationsquelle, bei der Django IoC verwendet?
Sajuuk
@Sajuuk, ich habe das über Django im Thread dieser Frage erfahren, also weiß ich nicht, du solltest die anderen Antwortautoren fragen.
Zakmck
Der erste Alinea dieser Antwort fügt meiner Meinung nach 0 hinzu ... Ich denke, ich kann entscheiden, wann mein Python-Code von IoC profitieren würde, und es ist mir egal, was Entwickler für schlecht halten. Ich schätze Pragmatismus gegenüber unbegründeten Meinungen.
Mike de Klerk
@MikedeKlerk Mein Vorschlag ist, dass etwas, das sowohl unbekannt ist (wie viele Antworten hiermit beweisen) als auch Opfer von Vorurteilen wird, wahrscheinlich nicht populär wird, egal wie objektiv und gut informiert einige wie Sie sind. Und natürlich bin ich mir nicht sicher, ob dies ein Grund ist, warum Sie in Python nicht viele IoC-Anwendungen sehen. Ich denke, der Hauptgrund ist, dass Apps mit geringer / mittlerer Komplexität sie nicht benötigen.
Zakmck
The typical Python application is much simpler, just a bunch of scripts, without such a complex architecture.- eine ziemliche Annahme
Hyankov
1

pytest fixtures alle basierend auf DI ( Quelle )

Meng Zhao
quelle
-1

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]

Santiagobasulto
quelle
-3

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.

Kylotan
quelle
4
Es ist nicht so sehr der Rahmen als die Sprache selbst. Statisch typisierte Sprachen benötigen sehr ausgefeilte Frameworks und Regeln, um die Flexibilität zu schaffen, die Entensprachsprachen genießen. DI ist eine dieser Regeln. Python-Leute denken nicht zweimal darüber nach. Java-Leute müssen wirklich daran arbeiten.
S.Lott
6
@ S.Lott - Ich stimme Ihnen voll und ganz zu, außer dass C ++ - Leute ohne die Explosion von Design- und Architekturmustern auszukommen scheinen, obwohl sie mit ähnlichen Einschränkungen wie Java arbeiten. Ich denke, das impliziert einen kulturellen Unterschied, bei dem Java-Leute, wenn sie mit zwei möglichen Möglichkeiten konfrontiert werden, lieber eine andere Schnittstelle extrahieren, um das Strategiemuster zu vereinfachen, während C ++ - Leute direkt eintauchen und einen Bool und eine if-Anweisung hinzufügen ...
Kylotan
3
@Finglas Wenn ich also ein Dutzend Klassen habe, die alle meine verwenden EmailSenderund beschließen, sie durch eine zu ersetzen DesktopNotifier, muss ich 12 Klassen von Hand bearbeiten. Und Sie denken, das ist einfacher und sauberer, als nur auf eine INotifierSchnittstelle zu schreiben und den Container die Details herausarbeiten zu lassen?
Basic
1
Leider ist ein gewisses Maß an Komplexität eine Realität, der sich professionelle Softwareentwickler stellen müssen. Ich sehe Kritik, aber keine Lösungen in dieser Antwort. Was ist die "pythonische" Lösung für dieses Problem: Ich schreibe eine Bibliothek und möchte einen Hook für die Protokollierung bereitstellen (so etwas wie das PSR-3 LoggerInterface von PHP). Ich weiß, wie man die Protokollebenen verwendet, aber es ist mir egal, wie das Programm sie tatsächlich meldet. Was ist die saubere Art und Weise der Client - Anwendung zu ermöglichen , zu injizieren , dass die Umsetzung Detail. Hinweis: Andere Teile der Anwendung haben möglicherweise andere Implementierungen dieser Schnittstelle.
Rob
2
Meine Frage an Sie lautet weder, wie Sie die Standardprotokollierungsbibliothek verwenden, noch geht es darum, verschiedene Instanzen einer Protokollierungsklasse zu erstellen. Meine Frage ist, wie Sie Ihre Anwendung so konfigurieren, dass verschiedene Teile Ihrer Anwendung unterschiedliche Implementierungen verwenden können und sich nicht mit diesen Details befassen (vorausgesetzt, sie wissen, wie die Schnittstelle verwendet wird). Dies ist ein sehr reales Problem, das DI für mehrere PHP-Anwendungen gelöst hat, an denen ich gearbeitet habe. Ich suche das Python-Äquivalent. Und der Vorschlag "Machen Sie Ihre Bewerbung einfach nicht so komplex" ist nicht die Antwort, nach der ich suche.
Rob
-5

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.

Jason Ching
quelle
4
Sie müssen noch etwas finden, das quakt.
Andho