it-swarm.com.de

Warum ist IoC / DI in Python nicht üblich?

In Java IoC / DI ist eine sehr verbreitete Praxis, die häufig in Webanwendungen verwendet wird , fast alle verfügbaren Frameworks und Java EE. Andererseits gibt es auch viele große Python Webanwendungen, aber neben Zope (das ich ' Ich habe gehört, dass Code wirklich schrecklich sein sollte.) IoC scheint 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 von populären Java IoC-Frameworks, die für Python verfügbar sind, Springpython . Aber keines davon scheint praktisch zu funktionieren. Ich bin noch nie auf eine Django oder sqlalchemy + <insert your favorite wsgi toolkit here> basierte Webanwendung gestoßen, die so etwas verwendet.

Meiner Meinung nach hat IoC vernünftige Vorteile und würde es zum Beispiel leicht machen, das Django-Standardbenutzermodell zu ersetzen, aber eine umfangreiche Verwendung von Schnittstellenklassen und IoC in Python sieht etwas seltsam aus und nicht "Pythonic": Aber vielleicht hat jemand eine bessere Erklärung, warum IoC in Python nicht weit verbreitet ist.

281
tux21b

Ich denke nicht wirklich, dass DI/IoC das sind, das in Python ungewöhnlich ist. Was ist ungewöhnlich, sind jedoch DI/IoC Frameworks/Container .

Denken Sie darüber nach: Was macht ein DI-Container? Es erlaubt Ihnen zu

  1. verdrahten Sie unabhängige Komponenten zu einer kompletten Anwendung ...
  2. ... zur Laufzeit.

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

  1. scripting
  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 stumpfer, 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 dann eine hässliche, schlechte Skriptsprache verwenden, wenn Sie über Jython und IronPython verfügen?

Um es noch einmal zusammenzufassen: Die Übung von DI/IoC ist in Python) genauso wichtig wie in Java. aus genau den gleichen Gründen: Die Implementierung von DI/IoC ist jedoch in die Sprache eingebaut und oft so leicht, dass sie vollständig verschwindet.

(Hier ein kurzer Überblick über eine Analogie: In Assembly ist ein Unterprogrammaufruf ein ziemlich wichtiger Vorgang - Sie müssen Ihre lokalen Variablen und Register im Speicher speichern, Ihre Rücksprungadresse irgendwo speichern und den Befehlszeiger auf das Unterprogramm ändern, das Sie aufrufen. Sorgen Sie dafür, dass es am Ende irgendwie wieder in Ihre Subroutine zurückspringt, platzieren Sie die Argumente an einer Stelle, an der der Angerufene sie finden kann usw. IOW: In Assembly ist "Subroutinenaufruf" ein Entwurfsmuster, und bevor es Sprachen wie dieses gab In Fortran, in dem Unterprogrammaufrufe eingebaut waren, bauten die Leute ihre eigenen "Unterprogramm-Frameworks". Würden Sie sagen, dass Unterprogrammaufrufe in Python "ungewöhnlich" sind, nur weil Sie keine Unterprogramm-Frameworks verwenden?)

Übrigens: Ein Beispiel dafür, wie es aussieht, DI zu seiner logischen Schlussfolgerung zu führen, finden Sie in Gilad Bracha s Newspeak Programming Language und seinen Schriften zu diesem Thema:

187
Jörg W Mittag

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 konstruiertes/gefülltes Objekt erhalten.

Dies steht im Gegensatz zu Java, bei dem Sie keine tatsächlichen Instanzen von Objekten importieren. Dies bedeutet, dass Sie sie immer selbst instanziieren müssen (oder eine Art 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. Sie müssen jedoch weiterhin jedes Mal Ressourcen aufwenden, um neue zu erstellen.

46
TM.

Django macht großen Gebrauch von der Umkehrung der Kontrolle. Beispielsweise wird der Datenbankserver in der Konfigurationsdatei ausgewählt, und das Framework stellt den Datenbankclients die entsprechenden Datenbank-Wrapper-Instanzen zur Verfügung.

Der Unterschied besteht darin, dass Python erstklassige Typen hat. Datentypen, einschließlich Klassen, sind selbst Objekte. Wenn Sie eine bestimmte Klasse verwenden möchten, benennen Sie die Klasse einfach. 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 Folgendes schreibt:

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

Anstelle der Boilerplate-Factory-Funktionen, die Java und C++ benötigen, wird Python mit ein oder zwei Zeilen normalen Codes ausgeführt. Dies ist die Stärke von funktional versus imperativ Programmierung.

36
Daniel Newby

IoC und DI sind in ausgereiftem Python Code sehr verbreitet. Sie brauchen nur kein Framework, um DI zu implementieren.

Das beste Beispiel ist, wie Sie eine Django Anwendung mit settings.py Einrichten:

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

30
Max Malysh

Habe seit einigen Jahren nicht mehr Python verwendet, aber ich würde sagen, dass es mehr damit zu tun hat, dass es sich um eine dynamisch typisierte Sprache handelt als um alles andere. Zum Beispiel in Java, wenn ich wollte Um zu testen, ob etwas richtig geschrieben wurde, konnte ich DI verwenden und einen beliebigen PrintStream übergeben, um den geschriebenen Text zu erfassen und zu überprüfen. Wenn ich in Ruby arbeite, kann ich jedoch die Methode 'puts' in STDOUT to dynamisch ersetzen Führen Sie die Überprüfung durch und lassen Sie DI komplett aus dem Bild. 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), dann erzeugt DI/IoC unnötige Komplexität in der Lösung.

12
bcarlso

IoC/DI ist ein Entwurfskonzept, wird aber leider 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 ohne viel Rücksicht auf "The Python Way" zu sein.

Angesichts der Anmerkungen in Python 3) habe ich mich für einen Crack in einem voll ausgestatteten, aber einfachen Abhängigkeitsinjektionscontainer entschieden: https://github.com/zsims/dic Es basiert auf einigen Konzepten aus einem .NET Dependency Injection Container (was IMO fantastisch ist, wenn Sie jemals in diesem Bereich spielen), aber mutiert mit Python Konzepten.

9
zsims

Es sieht so aus, als ob die Leute nicht mehr wirklich verstehen, was Abhängigkeitsinjektion und Umkehrung der Kontrolle bedeuten.

Bei der Verwendung der Invertierung der Steuerung werden Klassen oder Funktionen verwendet, 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 erzielt werden kann. Das hat viele Vorteile, da es mehr Testbarkeit und das Prinzip der Liskov-Substitution gibt.

Sie sehen, durch die Arbeit mit Interfaces und Injections wird Ihr Code wartungsfreundlicher, da Sie das Verhalten leicht ändern können, da Sie keine einzige Codezeile (möglicherweise ein oder zwei Zeilen in der DI-Konfiguration) neu schreiben müssen class, um das 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, besteht darin, mindestens die Prinzipien der Einzelverantwortung, Substitution und Abhängigkeitsinversion zu befolgen.

Wofür eignet sich eine DI-Bibliothek, wenn Sie ein Objekt in einem Paket selbst instanziieren und importieren können, um es selbst zu injizieren? Die gewählte Antwort ist richtig, da Java hat keine prozeduralen Abschnitte (Code außerhalb von Klassen), alles, was in langweilige Konfigurations-XMLs geht, daher die Notwendigkeit einer Klasse, Abhängigkeiten von einem faulen zu instanziieren und zu injizieren Laden Sie fashion, damit Sie Ihre Performance nicht in die Knie zwingen, während Sie bei python=) nur die Einfügungen in die "prozeduralen" (Code außerhalb der Klassen) Abschnitte Ihres Codes codieren

8

Tatsächlich ist es mit DI recht einfach, ausreichend sauberen und kompakten Code zu schreiben (ich frage mich, ob es dann/bleibt Pythonic , aber trotzdem :)) Zum Beispiel bevorzuge ich 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 einfache Form der Parametrisierung von Funktionen/Klassen angesehen werden, aber es funktioniert. Vielleicht reichen auch hier Pythons Standardbatterien aus.

P.S. Ich habe auch ein größeres Beispiel für diesen naiven Ansatz unter Dynamische Auswertung einfacher boolescher Logik in Python veröffentlicht.

7
mlvljr

Ich antworte "Jörg W Mittag": "Die Python Implementierung von DI/IoC ist so leicht, dass es komplett verschwindet".

Schauen Sie sich das berühmte Beispiel von Martin Fowler an, das von Java nach Python: Python: Design_Patterns: Inversion_of_Control portiert wurde, um diese Aussage zu stützen

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
6
emilmont

Ich denke, aufgrund der Dynamik von python Leute sehen nicht oft die Notwendigkeit für ein anderes dynamisches Framework. Wenn eine Klasse vom neuen Stil 'Objekt' erbt, können Sie dynamisch eine neue Variable erstellen ( https://wiki.python.org/moin/NewClassVsClassicClass ).

d. h. In normaler 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 genau das sein, wonach Sie suchen.

d. h. 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()
4

Mein 2 Cent ist, dass Sie es in den meisten Python Anwendungen nicht benötigen und dass viele Java Hasser (und inkompetente Geiger, die glauben, Entwickler zu sein) es für etwas halten, selbst wenn Sie es benötigen schlecht, nur weil es in Java beliebt ist.

Ein IoC-System ist tatsächlich nützlich, wenn Sie komplexe Objektnetzwerke haben, in 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 sollten Sie alle diese Objekte einmal definieren und über einen Mechanismus verfügen, der sie automatisch zusammensetzt, 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 so etwas wie einer einfachen XML-Datei lesen kann (das wäre die Konfiguration).

Die typische Anwendung Python ist viel einfacher, nur eine Reihe von Skripten, ohne solch eine komplexe Architektur. Persönlich bin ich mir dessen bewusst, was eine 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 verspürt (auch ich benutze Spring nicht überall, nicht, wenn die Vorteile, die es bietet, den Entwicklungsaufwand nicht rechtfertigen.

Das heißt, es gibt 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 wie oben könnte auf aspektorientierte Programmierung in der Welt Java angewendet werden, mit dem Unterschied, dass die Anzahl der Fälle, in denen sich AOP wirklich lohnt, noch geringer ist.

3
zakmck