6 Stimmen

Wohlwollend-abbauendes Beizen in Python

(Sie können lesen este Frage für einige Hintergrundinformationen)

Ich möchte eine anmutig-abbauende Möglichkeit, Objekte in Python zu beherbergen.

Beim Beizen eines Objekts, nennen wir es das Hauptobjekt, löst der Beizer manchmal eine Ausnahme aus, weil er ein bestimmtes Unterobjekt des Hauptobjekts nicht beizen kann. Eine Fehlermeldung, die ich häufig erhalte, lautet zum Beispiel "can't pickle module objects". Das liegt daran, dass ich ein Modul aus dem Hauptobjekt referenziere.

Ich weiß, dass ich etwas schreiben kann, um das Modul durch eine Fassade zu ersetzen, die die Attribute des Moduls enthalten würde, aber das würde seine eigenen Probleme haben(1).

Was ich also gerne hätte, ist eine Pickling-Funktion, die automatisch Module (und alle anderen schwer zu pickenden Objekte) durch Fassaden ersetzt, die ihre Attribute enthalten. Das führt vielleicht nicht zu einer perfekten Beizung, aber in vielen Fällen wäre es ausreichend.

Gibt es so etwas? Hat jemand eine Idee, wie man das angehen könnte?


(1) Ein Problem könnte darin bestehen, dass das Modul innerhalb des Moduls auf andere Module verweist.

3voto

Alex Martelli Punkte 805329

Sie können entscheiden und implementieren, wie jeder zuvor nicht-beizbare Typ gebeizt und entbeizt wird: siehe Standardbibliotheksmodul copy_reg (umbenannt in copyreg in Python 3.*).

Im Wesentlichen müssen Sie eine Funktion bereitstellen, die eine Instanz des Typs auf ein Tupel reduziert - mit demselben Protokoll wie die reduzieren. spezielle Methode (mit dem Unterschied, dass die spezielle Methode reduce keine Argumente benötigt, da sie, wenn sie bereitgestellt wird, direkt auf dem Objekt aufgerufen wird, während die Funktion, die Sie bereitstellen, das Objekt als einziges Argument benötigt).

Das Tupel, das Sie zurückgeben, besteht in der Regel aus 2 Elementen: einem Callable und einem Tupel von Argumenten, die an das Callable übergeben werden. Das Callable muss als "sicherer Konstruktor" registriert sein oder gleichwertig über ein Attribut __safe_for_unpickling__ mit einem wahren Wert. Diese Elemente werden gepickelt, und zum Zeitpunkt des Entpickelns wird die Callable mit den angegebenen Argumenten aufgerufen und muss das entpickelte Objekt zurückgeben.

Nehmen wir zum Beispiel an, dass Sie Module nur nach Namen kommissionieren wollen, so dass das Entpacken nur ein erneutes Importieren bedeutet (d.h. nehmen wir der Einfachheit halber an, dass Sie sich nicht um dynamisch modifizierte Module, verschachtelte Pakete usw. kümmern, sondern nur um einfache Top-Level-Module). Dann:

>>> import sys, pickle, copy_reg
>>> def savemodule(module):
...   return __import__, (module.__name__,)
... 
>>> copy_reg.pickle(type(sys), savemodule)
>>> s = pickle.dumps(sys)
>>> s
"c__builtin__\n__import__\np0\n(S'sys'\np1\ntp2\nRp3\n."
>>> z = pickle.loads(s)
>>> z
<module 'sys' (built-in)>

Ich verwende die altmodische ASCII-Form von pickle, damit s , die Zeichenkette, die den Pickle enthält, ist leicht zu untersuchen: Sie weist unpickling an, die eingebaute Importfunktion aufzurufen, mit der Zeichenkette sys als einziges Argument. Und z zeigt, dass dies in der Tat zu einer Rückführung der eingebauten sys Modul als Ergebnis des Entpickelns, wie gewünscht.

Jetzt müssen Sie die Dinge etwas komplexer gestalten als nur __import__ (Sie müssen sich mit dem Speichern und Wiederherstellen von dynamischen Änderungen befassen, in einem verschachtelten Namensraum navigieren usw.), und daher müssen Sie auch die copy_reg.constructor (und übergibt als Argument Ihre eigene Funktion, die diese Arbeit ausführt), bevor Sie copy_reg die modulsparende Funktion, die Ihre andere Funktion zurückgibt (und, falls in einem separaten Durchlauf, auch bevor Sie die Gurken, die Sie mit der besagten Funktion hergestellt haben, entpickeln). Aber ich hoffe, dieser einfache Fall zeigt, dass es wirklich nicht viel gibt, was "an sich" kompliziert ist!-)

0voto

Jason R. Coombs Punkte 38667

Wie wäre es mit folgendem, einem Wrapper, mit dem Sie einige Module (vielleicht jedes Modul) in etwas verpacken können, das sich beizen lässt. Sie könnten dann das Pickler-Objekt unterklassifizieren, um zu prüfen, ob das Zielobjekt ein Modul ist, und wenn ja, es einpacken. Erreicht man damit, was man will?

class PickleableModuleWrapper(object):
    def __init__(self, module):
        # make a copy of the module's namespace in this instance
        self.__dict__ = dict(module.__dict__)
        # remove anything that's going to give us trouble during pickling
        self.remove_unpickleable_attributes()

    def remove_unpickleable_attributes(self):
        for name, value in self.__dict__.items():
            try:
                pickle.dumps(value)
            except Exception:
                del self.__dict__[name]

import pickle
p = pickle.dumps(PickleableModuleWrapper(pickle))
wrapped_mod = pickle.loads(p)

0voto

Chinmay Kanchi Punkte 58341

Hmmm, so etwas wie das hier?

import sys

attribList = dir(someobject)
for attrib in attribList:
    if(type(attrib) == type(sys)): #is a module
        #put in a facade, either recursively list the module and do the same thing, or just put in something like str('modulename_module')
    else:
        #proceed with normal pickle

Offensichtlich würde dies in eine Erweiterung der Pickle-Klasse mit einer neu implementierten Dump-Methode gehen...

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