17 Stimmen

Gibt es einen Punkt zu Abhängigkeit Injektion Container in Python?

Ich habe mit Python herumgespielt, und wie üblich wirft es meine starre, statisch typisierte, objektorientierte Welt ein wenig durcheinander. Python unterstützt die Duck-Typisierung, hat kein brauchbares Konzept der schnittstellenbasierten Programmierung (wie in C#-Schnittstellen) und erlaubt globale Variablen. Gibt es bei all diesen Vorzügen wirklich einen Sinn für einen Dependency Injection Container oder wird die Python-Laufzeit zum Container.

Ich verstehe den Sinn dieser Container in statisch typisierten OO-Sprachen wie Java und C#, aber wo würde so etwas in die verrückte Welt von Python (ich liebe es) passen?

Ich habe immer vermutet, dass Dependency Injection als Entwurfsmuster ein schlechter Geruch war, die durch alles muss eine Klasse "Nazi-Denken", die c# und Java ist erstellt wurde, wäre ich richtig oder gibt es etwas, das ich vermisse?

Bisher denke ich, dass ich Fabriken, Singletons, Multi-Instanz-Objekte, nur durch die Verwendung von Globals abdecken kann. Ich vermute auch, dass die Aspect Zeug ist auch abgedeckt, obwohl ich noch darüber nachdenken.

Die Duck Typing ist die Sache, die mich im Moment, so verwendet, um Schnittstellen zu definieren, dann basierend Klassen auf diesen Schnittstellen und lassen Sie die statische Sachen decken meine Dummheit, dass ich das Gefühl, dass ohne statische Typisierung, Container sind ein bisschen nutzlos.

bearbeiten

Ich denke, ich werde nicht mit Dependency Injector Frameworks/Container, wenn mit Python. Es hat wirklich keinen Sinn. Nachdem ich nachgedacht und die bisherigen Antworten gelesen habe, ist das Argument klar, dass ohne statische Typdefinitionen die Versprechungen, die gemacht werden, so lose sind, dass man sich die Mühe gar nicht machen sollte. Duck Typing ist, was es ist, und das einzige Versprechen kann durch Dokumentation gegeben werden. Solange der Verweis in der Klasse Methode / Funktion durch einen Parameter signiture kommt, und nicht durch den Äther der Programmierumgebung kommen, dann denke ich, ich werde sicher sein.

Beunruhigend ist jedoch die Tatsache, dass ich anderen durch meine überzogenen Designpraktiken nicht meinen Willen aufzwingen kann, wie ich es in Java und C# getan habe. Ist mir das egal......nah :)

13voto

Karl Knechtel Punkte 55450

hat kein brauchbares Konzept der schnittstellenbasierten Programmierung (wie bei C#-Schnittstellen)

Nur weil der Compiler nicht überprüfen kann, ob Sie die Schnittstelle korrekt verwenden, heißt das nicht, dass es "kein brauchbares Konzept für Schnittstellen" gibt. Man dokumentiert eine Schnittstelle und schreibt Unit-Tests.

Was die Globals betrifft, so ist es nicht wie public static Methoden und Felder von C#- oder Java-Klassen wirklich unterschiedlich sind. Betrachten Sie zum Beispiel, wie java.lang.Math funktioniert. Betrachten Sie nun die Tatsache, dass java.lang.Math ist nicht ein Singleton. Das haben sie aus gutem Grund getan.

Mit all diesen Vorzügen ist es wirklich sinnvoll, einen Container für die Injektion von Abhängigkeiten zu verwenden.

Ich bezweifle es, aber ich habe auch nie wirklich den Sinn von ihnen in C# oder Java gesehen. Dependency Injection ist eine Programmierung Technik meiner Meinung nach. Und es ist auch nicht wirklich viel dabei.

Ich habe immer vermutet, dass Dependency Injection als Entwurfsmuster ein übler Geruch war, der von allem, was eine Klasse "Nazi-Denken" sein muss, erzeugt wurde.

Nein, ist es nicht. Dependency Injection ist in den meisten Fällen eine gute Idee. Sie brauchen auch keine Klasse, in die Sie Abhängigkeiten injizieren. Jedes Mal, wenn Sie einer freien Funktion etwas als Parameter übergeben, anstatt die Funktion eine andere Funktion aufrufen zu lassen, um die Informationen zu erhalten, tun Sie im Grunde das Gleiche: Umkehrung der Kontrolle. In Python können Sie Module in vielerlei Hinsicht ähnlich wie Klassen behandeln (auf jeden Fall mehr als in Java und C#). Es gibt Probleme, die gelöst werden können, indem man Module als Parameter an Funktionen übergibt :)

Bisher denke ich, dass ich Fabriken, Singletons, Multi-Instanz-Objekte, nur durch die Verwendung von Globals abdecken kann.

Singletons sind der schlechte Geruch, wenn überhaupt. In fast jedem Fall, in meiner umfangreichen Erfahrung, existieren sie, weil jemand dachte, es wäre prinzipiell schlecht(TM), ein Global zu haben, ohne wirklich über die möglichen Optionen nachzudenken, oder warum sie diese Art von Zugriff auf ein einzelnes gemeinsames Objekt wollten, oder sogar, warum Globals überhaupt "prinzipiell schlecht(TM)" sind.

Sie könnte eine globale Funktion in Python erstellen, die als Fabrik fungiert. Ich würde jedoch sagen, dass es pythonischer ist, eines der folgenden Dinge zu tun:

a) erstens, wirklich, wirklich machen, wirklich sicher, dass Sie nicht einfach tun können, was Sie wollen, mit __init__ . Ich meine, in einer dynamisch typisierten Sprache kann man auf diese Weise eine ganze Menge erreichen.

b) Wenn __init__ nicht ausreicht, versuchen Sie es mit __new__ um das Verhalten zu kontrollieren.

In Python sind die Klassen selbst Objekte, die aufrufbar sind. Standardmäßig wird durch den Aufruf die Klasse instanziiert. Mit __new__ können Sie sich da einklinken.

c) Verwenden Sie einen Dekorator, der auf die Klasse angewendet wird. Hier ist ein Beispiel, das ein Singleton macht (nur so):

def _singleton(cls):
  instance = cls()
  result = lambda: instance
  result.__doc__ = cls.__doc__
  return result

@_singleton
class example(object): pass

Das funktioniert so: Wenn Sie die Klasse dekorieren, _singleton() aufgerufen wird, wobei die Klasse übergeben wird. Eine Instanz wird erstellt und zwischengespeichert, und _singleton() gibt eine anonyme Funktion zurück, die beim Aufruf die Instanz zurückgibt. Um die Scharade zu vollenden, wird die Dokumentation der Klasse an die anonyme Funktion angehängt. Dann bindet Python den Namen der Klasse im globalen Bereich wieder an die zurückgegebene anonyme Funktion. Wenn Sie die Funktion also aufrufen, erhalten Sie jedes Mal dieselbe Instanz der Klasse.

Dies kann natürlich immer noch umgangen werden (Sie können z. B. Folgendes tun example().__class__() um eine andere Instanz zu erhalten), aber es ist viel deutlicher, dass Sie etwas falsch machen, als wenn Sie einfach eine Fabrikfunktion ignorieren, um den Konstruktor normal zu verwenden. Außerdem bedeutet dies, dass der aufrufende Code sich tatsächlich so verhält, als ob er den Konstruktor normal aufrufen würde :)

Die Duck Typing ist die Sache, die mich im Moment, so verwendet, um Schnittstellen zu definieren, dann basierend Klassen auf diesen Schnittstellen und lassen Sie die statische Sachen decken meine Dummheit, dass ich das Gefühl, dass ohne statische Typisierung, Container sind ein bisschen nutzlos.

Sie müssen umdenken: Hören Sie auf, sich darüber Gedanken zu machen, was die Sache, die Ihnen zugesprochen wurde es und sorgen sich um ob es das tun kann, was Sie wollen . So funktioniert das Tippen von Enten.

8voto

cwallenpoole Punkte 75701

Die Schlussfolgerung

(Das Folgende ist der wichtigste Teil des ursprünglichen Beitrags. Ich gebe zu, dass ich ein wenig poetisch geworden bin, und deshalb dachte ich, ich sollte die wichtigsten Sätze einfach in einen eigenen Abschnitt aufnehmen. Dennoch halte ich das poetische Wachsen für so wichtig, dass ich es nicht gelöscht habe).

Dependency Injection wird immer noch verwendet. Es wird immer die Notwendigkeit bestehen, dass Objekte mit bereits instanziierten Objekten kommunizieren. Es wird notwendig sein, dass "Eltern"-Objekte (oder Container, oder was auch immer) den Zustand ihrer "Kinder" einrichten. Diese müssen durch eine Methode übergeben oder durch eine Zuweisung festgelegt werden, aber im abstrakten Sinne ist es das Gleiche.

Die ursprüngliche Antwort:

Typische Systeme

Python ist in vielerlei Hinsicht die zweitreinste mathematische Sprache, die mir je begegnet ist - sie folgt nur Scheme (obwohl ich Haskell noch nie benutzt habe, aber soweit ich weiß, ist es auch dort oben).

Python unterstützt von Haus aus Abschlüsse, es gibt Fortsetzungen (Yield-Syntax), Mehrfachvererbung und eine Syntax für das Verstehen von Schleifen, die weitgehend unerreicht ist. All dies bringt Python der ursprünglichen Vision von Alonzo Church in Lambda Calculus (und den ursprünglichen Ideen von McCarthy hinter Lisp) sehr viel näher. Python 3 macht es sogar noch reiner mit Set Comprehensions (die mein Herz flattern lassen, wenn ich nur an ihre inhärente Schönheit denke).

Immer wieder taucht der Gedanke auf, dass die in den Variablen von Python gespeicherten Daten mehr mit ihren Gegenstücken in der Mathematik gemeinsam haben, so sehr, dass die Schnittstelle eines Objekts einfach auf "die Adjektive oder die Menge der Adjektive, die das Objekt beschreiben" reduziert werden kann. Im Grunde genommen ist die Schnittstelle eines Objekts vollständig in seiner __dict__ und angezeigt mit dir .

Alles in allem stellt sich die Frage, ob die "traditionelle" Sichtweise ("traditionell" in Anführungszeichen, denn Python ist so alt wie Java) in einer solchen Sprache wirklich funktionieren kann. Wenn sogar Argumentlisten in Python ein OOP-Gefühl haben (kwargs, anyone?), stellt das die Welt wirklich auf den Kopf.

Dependency Injection wird immer noch verwendet. Es wird immer die Notwendigkeit bestehen, dass Objekte mit bereits instanziierten Objekten kommunizieren. Es wird notwendig sein, dass "Eltern"-Objekte (oder Container, oder was auch immer) den Zustand ihrer "Kinder" einrichten. Diese müssen durch eine Methode übergeben oder durch eine Zuweisung gesetzt werden, aber im abstrakten Sinne ist es das Gleiche. Es besteht jedoch ein implizites Vertrauen darauf, dass der Inhalt des übergebenen Objekts __dict__ wird eine entsprechende Beschreibung enthalten. Aus diesem Grund heißt es weniger: "Sobald ich dieses Objekt instanziiert habe, werde ich ihm genau die Dinge geben, die es zum Leben braucht", sondern vielmehr: "Nun ja, ein Objekt braucht einen Zustand, also gebe ich ihm einen."

Dies zeigt einen verborgenen Aspekt der statischen Typisierung auf. Damit etwas, das eine IFoo um zu funktionieren, muss sie umfassend und vollständig wissen, was es bedeutet, ein Mensch zu sein IFoo auch wenn sie niemals 90 % dieser Definition benötigen wird. Bei der Duck-Typisierung weiß das abhängige Objekt nur, dass die Eigenschaften X, Y und Z zur Laufzeit vorhanden sein müssen.

Globales

Zu den Globals. Vermeiden Sie sie, es sei denn, Sie haben keine andere Wahl. Sie sind besser dran mit einem Singleton, wenn auch nur, weil Singletons können Sie protokollieren, wenn der Wert ändert. Dies ist bei Globals nicht der Fall.

Es gibt eine Regel in der Programmierung: Je offener und komplizierter eine Schnittstelle ist, desto schwieriger ist sie zu warten. Wenn etwas in einer globalen Variable platziert wird, alles in diesem Modul, und möglicherweise alles en ANY Modul kann den Wert ändern. Der Code kann fast nicht-deterministisch werden. Ich versichere Ihnen, es folgt Traurigkeit.

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