Abnehmen einer Funktion in einen anderen Kontext in Python

8

Ich habe eine Python-Schnittstelle für ein prozessorientiertes Jobverteilungssystem geschrieben, das wir intern an meinem Arbeitsplatz entwickeln / verwenden. Obwohl die Programmierkenntnisse einigermaßen gut sind, sind die primären Leute, die diese Schnittstelle verwenden, Forscher und keine Softwareentwickler, so dass die Benutzerfreundlichkeit und die größtmögliche Ausschließung der Schnittstelle von größter Bedeutung sind.

Meine Bibliothek entrollt eine Sequenz von Eingaben in eine Sequenz von Pickle-Dateien auf einem gemeinsam genutzten Dateiserver und erzeugt dann Jobs, die diese Eingaben laden, die Berechnung durchführen, die Ergebnisse picken und beenden; Das Client-Skript ruft dann eine Sicherungskopie auf und erzeugt einen Generator, der die Ergebnisse lädt und liefert (oder jede Ausnahme, die die Berechnungsfunktion vorgenommen hat, erneut ausführt).

Dies ist nur nützlich, da die Berechnungsfunktion selbst einer der serialisierten Eingänge ist. cPickle ist ziemlich zufrieden, Funktionsreferenz zu pikeln, aber erfordert, dass die in Essig eingelegte Funktion im selben Kontext wieder importiert werden kann. Dies ist problematisch. Ich habe bereits das Problem gelöst, das Modul zu finden, um es neu zu importieren, aber die meiste Zeit ist es eine Funktion der obersten Ebene, die gebeizt ist und daher keinen Modulpfad hat. Die einzige Strategie, die ich gefunden habe, um eine solche Funktion auf den Rechenknoten zu entkoppeln, ist dieser ekelerregende kleine Ansatz, die ursprüngliche Umgebung zu simulieren, in der die Funktion gebeizt wurde, bevor sie wieder geöffnet wurde:

%Vor%

Die letzte Zeile ist hier die wichtigste. Dort nimmt mein Modul die Funktion auf, die es eigentlich ausführen soll. Dieser Code, wie geschrieben, funktioniert wie gewünscht, aber die Manipulation der Symboltabellen direkt, wie dies beunruhigend ist.

Wie kann ich das machen oder so etwas, das die Forscher nicht zwingt, ihre Berechnungsskripte in eine geeignete Klassenstruktur zu unterteilen (sie benutzen Python wie den besten Graphikrechner überhaupt und ich würde gerne weitermachen Lass sie das tun) wie Pickle verzweifelt will, ohne die unangenehme, unsichere und einfach gruselige __dict__ -and- globals() Manipulation, die ich oben verwende? Ich bin felsenfest davon überzeugt, dass es einen besseren Weg geben muss, aber exec "from {0} import *".format("modname") hat es nicht getan, mehrere Versuche, die Beizladung in die Referenz targetModule zu injizieren, haben es nicht getan, und eval("commonUnpickle.load()", targetModule.__dict__, locals()) hat es nicht getan. All dies scheitert mit Unpickles AttributeError over, da die Funktion in <module> nicht gefunden werden kann.

Was ist ein besserer Weg?

    
Adam Norberg 18.08.2011, 19:25
quelle

4 Antworten

2

Beißfunktionen können ziemlich nervig sein, wenn Sie versuchen, sie in einen anderen Kontext zu verschieben. Wenn die Funktion nichts von dem Modul referenziert, in dem sie sich befindet, und auf (wenn überhaupt) auf Module verweist, die garantiert importiert werden, können Sie einen Code von Rudimentäre Datenbank-Engine im Python-Kochbuch gefunden.

Um Ansichten zu unterstützen, greift das akademische Modul den Code aus der aufrufbaren Ebene, wenn die Abfrage angekreuzt wird. Wenn es Zeit wird, die Ansicht abzuwickeln, wird eine LambdaType-Instanz mit dem Codeobjekt und einem Verweis auf einen Namespace erstellt, der alle importierten Module enthält. Die Lösung hat Einschränkungen, funktioniert aber gut genug für die Übung.

Beispiel für Ansichten

%Vor%

Manchmal erscheint es notwendig, Änderungen an den registrierten Modulen vorzunehmen, die im System verfügbar sind. Wenn Sie beispielsweise auf das erste Modul ( __main__ ) verweisen müssen, müssen Sie möglicherweise ein neues Modul erstellen, bei dem Ihr verfügbarer Namespace in ein neues Modulobjekt geladen wird. Das gleiche Rezept verwendete die folgende Technik.

Beispiel für Module

%Vor%
Noctis Skytower 18.08.2011 19:57
quelle
1

Damit ein Modul als geladen erkannt wird, muss es in sys.modules vorliegen, nicht nur sein Inhalt muss in Ihren globalen / lokalen Namespace importiert werden. Versuchen Sie, alles auszuführen und dann das Ergebnis aus einer künstlichen Umgebung herauszuholen.

%Vor%     
Jürgen Strobel 18.08.2011 19:43
quelle
0

Während Funktionen in Python als erstklassige Objekte angekündigt werden, ist dies ein Fall, in dem man sieht, dass sie wirklich Objekte der zweiten Klasse sind. Es ist der Verweis auf das Callable, nicht das Objekt selbst, das gebeizt wird. (Sie können einen lambda -Ausdruck nicht direkt pikieren.)

Es gibt eine alternative Verwendung von __import__ , die Sie bevorzugen könnten:

%Vor%

Das wären also alle gleichwertig:

%Vor%     
wberry 18.08.2011 20:04
quelle
0

Ihre Frage war lang, und ich war zu koffeiniert, um Ihre lange Frage zu beantworten ... Ich denke jedoch, dass Sie etwas tun wollen, für das es bereits eine ziemlich gute bestehende Lösung gibt. Es gibt eine Verzweigung der Bibliothek parallel python (d. H.% Co_de%), die Funktionen und Objekte übernimmt und serialisiert, sie an verschiedene Server sendet und sie dann dekomprimiert und ausführt. Der Fork lebt im pp -Paket, aber Sie können es hier herunterladen:

Ссылка

Der "andere Kontext" ist in diesem Fall ein anderer Server ... und die Objekte werden transportiert, indem die Objekte in den Quellcode und dann zurück in Objekte konvertiert werden.

Wenn Sie das Beizen verwenden möchten, so wie Sie es bereits tun, gibt es eine Erweiterung auf pathos , die Argumente und Funktionen serialisiert und gebeizte Rückgabewerte zurückgibt ... Das Paket heißt mpi4py und wird häufig verwendet verwendet, um Code und Objekte an Cluster-Knoten in Koordination mit einem Cluster-Scheduler zu versenden.

Sowohl pyina als auch pathos stellen pyina abstractions (und map ) zur Verfügung und versuchen, alle Details des parallelen Rechnens hinter den Abstraktionen zu verbergen, so dass die Wissenschaftler nichts lernen müssen, außer wie Programm normale serielle Python. Sie verwenden nur eine der Funktionen pipe oder map und erhalten paralleles oder verteiltes Computing.

Oh, ich hätte es fast vergessen. Der pipe Serializer enthält die Funktionen dill und dump_session , die es dem Benutzer ermöglichen, die gesamte Interpreter-Sitzung einfach zu serialisieren und sie an einen anderen Computer zu senden (oder einfach für die spätere Verwendung zu speichern). Das ist ziemlich praktisch, um Kontexte in einem anderen Sinne zu ändern.

Erhalte load_session , dill und pathos hier: Ссылка

    
Mike McKerns 22.07.2014 15:59
quelle

Tags und Links