Frage

ich ein Objekt gui_project haben, die ein Attribut .namespace hat, die ein Namespace dict ist. (Das heißt ein dict von Strings zu Objekten.)

(Dies ist in einem IDE-ähnlichen Programm verwendet der Anwender festlegen, sein eigenes Objekt in einem Python-Shell zu lassen.)

Ich mag diese gui_project beizen, zusammen mit dem Namespace. Das Problem ist, einige Objekte im Namensraum (das heißt Wert der .namespace dict) nicht picklable Objekte. Zum Beispiel beziehen sich einige von ihnen zu WxPython Widgets.

Ich möchte die unpicklable Objekte herauszufiltern, das heißt, ausschließen sie von der eingelegten Version.

Wie kann ich das tun?

(Eine Sache habe ich versucht, ist eine nach der anderen auf den Werten zu gehen und versuchen, sie beizen, aber einige unendliche Rekursion passiert ist, und ich brauche aus, dass sicher zu sein.)

(I eine GuiProject.__getstate__ Methode tut implementieren gerade jetzt, von anderen unpicklable Sachen neben namespace loszuwerden.)

War es hilfreich?

Lösung 3

ich am Ende Codierung meine eigene Lösung für dieses Problem, mit Shane Hathaway Ansatz.

Hier ist der Code . (Suchen Sie nach CutePickler und CuteUnpickler.) Hier sind die Tests. Es ist Teil der GarlicSim , so dass Sie es von garlicsim und tun from garlicsim.general_misc import pickle_tools installieren.

Wenn Sie es auf Python 3-Code verwenden möchten, verwenden Sie die Python 3 Gabel von garlicsim .

Andere Tipps

würde ich die pickler dokumentierten Unterstützung für persistente Objektreferenzen verwenden. Persistent Objektreferenzen sind Objekte, die durch die Beize referenziert werden, aber nicht in der Gurke gespeichert.

http://docs.python.org/ Bibliothek / pickle.html # Beizen-und-Unpickling-external-Objekte

hat ZODB diese API seit Jahren verwendet, so ist es sehr stabil ist. Wenn Unpickling, können Sie die Objektreferenzen mit etwas ersetzen Sie mögen. In Ihrem Fall würden Sie die Objektreferenzen mit Markern zu ersetzen, was anzeigt, dass die Objekte nicht gebeizt werden.

Sie können mit so etwas wie dies starten (ungetestet):

import cPickle

def persistent_id(obj):
    if isinstance(obj, wxObject):
        return "filtered:wxObject"
    else:
        return None

class FilteredObject:
    def __init__(self, about):
        self.about = about
    def __repr__(self):
        return 'FilteredObject(%s)' % repr(self.about)

def persistent_load(obj_id):
    if obj_id.startswith('filtered:'):
        return FilteredObject(obj_id[9:])
    else:
        raise cPickle.UnpicklingError('Invalid persistent id')

def dump_filtered(obj, file):
    p = cPickle.Pickler(file)
    p.persistent_id = persistent_id
    p.dump(obj)

def load_filtered(file)
    u = cPickle.Unpickler(file)
    u.persistent_load = persistent_load
    return u.load()

Dann rufen Sie einfach dump_filtered () und load_filtered () anstelle von pickle.dump () und pickle.load (). WxPython Objekte werden als persistente IDs gebeizt werden, um Unpickling Zeit mit FilteredObjects ersetzt werden.

Sie könnte die Lösung genereller machen durch Herausfiltern von Objekten, die nicht von der eingebauten Typen sind und keine __getstate__ Methode.

Aktualisieren (15 Nov 2010): Hier ist ein Weg, um die gleiche Sache mit Wrapper-Klassen zu erreichen. Unter Verwendung von Wrapper-Klassen anstelle von Subklassen, ist es möglich, innerhalb der dokumentierten API zu bleiben.

from cPickle import Pickler, Unpickler, UnpicklingError


class FilteredObject:
    def __init__(self, about):
        self.about = about
    def __repr__(self):
        return 'FilteredObject(%s)' % repr(self.about)


class MyPickler(object):

    def __init__(self, file, protocol=0):
        pickler = Pickler(file, protocol)
        pickler.persistent_id = self.persistent_id
        self.dump = pickler.dump
        self.clear_memo = pickler.clear_memo

    def persistent_id(self, obj):
        if not hasattr(obj, '__getstate__') and not isinstance(obj,
            (basestring, int, long, float, tuple, list, set, dict)):
            return "filtered:%s" % type(obj)
        else:
            return None


class MyUnpickler(object):

    def __init__(self, file):
        unpickler = Unpickler(file)
        unpickler.persistent_load = self.persistent_load
        self.load = unpickler.load
        self.noload = unpickler.noload

    def persistent_load(self, obj_id):
        if obj_id.startswith('filtered:'):
            return FilteredObject(obj_id[9:])
        else:
            raise UnpicklingError('Invalid persistent id')


if __name__ == '__main__':
    from cStringIO import StringIO

    class UnpickleableThing(object):
        pass

    f = StringIO()
    p = MyPickler(f)
    p.dump({'a': 1, 'b': UnpickleableThing()})

    f.seek(0)
    u = MyUnpickler(f)
    obj = u.load()
    print obj

    assert obj['a'] == 1
    assert isinstance(obj['b'], FilteredObject)
    assert obj['b'].about

Dies ist, wie ich dies tun würde (ich habe etwas ähnliches vor und es funktionierte):

  1. Schreiben Sie eine Funktion, die bestimmt, ob ein Objekt ist pickleable
  2. Make
  3. eine Liste aller pickleable Variablen, basierend auf der obigen Funktion
  4. Erstellen Sie ein neues Wörterbuch (so genannte D), dass speichert alle nicht-pickleable Variablen
  5. Für jede Variable in D (das funktioniert nur, wenn Sie sehr ähnliche Variablen in d haben)    machen Sie eine Liste von Strings, wobei jeder String Recht Python-Code, so dass    wenn alle diese Strings in dieser Reihenfolge ausgeführt werden, können Sie die gewünschte Variable erhalten

Wenn Sie nun unpickle, Sie alle Variablen zurück, die ursprünglich pickleable waren. Für alle Variablen, die nicht pickleable waren, haben Sie jetzt eine Liste von Strings (Rechtskode Python), dass, wenn in der Reihenfolge ausgeführt, gibt Ihnen die gewünschte Variable.

Hope, das hilft

wäre ein Ansatz von pickle.Pickler zu erben sein, und die save_dict() Methode außer Kraft setzen. Kopieren Sie es von der Basisklasse, die wie folgt lautet:

def save_dict(self, obj):
    write = self.write

    if self.bin:
        write(EMPTY_DICT)
    else:   # proto 0 -- can't use EMPTY_DICT
        write(MARK + DICT)

    self.memoize(obj)
    self._batch_setitems(obj.iteritems())

Doch in der _batch_setitems, passiert ein Iterator, filtert alle Elemente, die Sie nicht wollen, abgeladen werden, z

def save_dict(self, obj):
    write = self.write

    if self.bin:
        write(EMPTY_DICT)
    else:   # proto 0 -- can't use EMPTY_DICT
        write(MARK + DICT)

    self.memoize(obj)
    self._batch_setitems(item for item in obj.iteritems() 
                         if not isinstance(item[1], bad_type))

Als save_dict keine offizielle API ist, müssen Sie für jede neue Python-Version überprüfen, ob diese Überschreibung noch korrekt ist.

Die Filterung Teil ist in der Tat schwierig. Mit einfachen Tricks können Sie bequem die Gurke an der Arbeit. Sie könnten jedoch auch zu viel am Ende herausfiltert und Informationen zu verlieren, dass Sie halten könnten, wenn die Filter ein wenig tiefer aussehen. Aber die große Möglichkeit der Dinge, die in der .namespace kann am Ende macht den Aufbau einer guten Filter schwierig.

Allerdings können wir Stücke nutzen, die bereits Teil von Python sind, wie deepcopy im copy Modul.

habe ich eine Kopie des Aktien copy Modul und hat die folgenden Dinge:

  1. erstellen Sie einen neuen Typ namens LostObject Objekt darzustellen, die in Beizen verloren.
  2. Änderung _deepcopy_atomic um sicherzustellen, dass x picklable ist. Wenn es nicht, gibt eine Instanz von LostObject
  3. Objekte können Methoden __reduce__ und / oder __reduce_ex__ definieren Hinweis zu liefern, ob und wie es beizen. Wir stellen sicher, dass diese Methoden nicht Ausnahme werfen Hinweis zu liefern, dass es nicht gebeizt werden kann.
  4. vermeiden unnötige Kopie des großen Objekts erzeugt werden ( a la tatsächliche deep), überprüfen wir rekursiv, ob ein Objekt picklable ist, und nur unpicklable Teil machen. Zum Beispiel für ein Tupel einer picklable Liste und und ein unpickable Objekt, werden wir eine Kopie des Tupels machen - nur um den Behälter -. Aber nicht seine Mitgliedsliste

Im Folgenden ist der Unterschied:

[~/Development/scratch/] $ diff -uN  /System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/copy.py mcopy.py
--- /System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/copy.py  2010-01-09 00:18:38.000000000 -0800
+++ mcopy.py    2010-11-10 08:50:26.000000000 -0800
@@ -157,6 +157,13 @@

     cls = type(x)

+    # if x is picklable, there is no need to make a new copy, just ref it
+    try:
+        dumps(x)
+        return x
+    except TypeError:
+        pass
+
     copier = _deepcopy_dispatch.get(cls)
     if copier:
         y = copier(x, memo)
@@ -179,10 +186,18 @@
                     reductor = getattr(x, "__reduce_ex__", None)
                     if reductor:
                         rv = reductor(2)
+                        try:
+                            x.__reduce_ex__()
+                        except TypeError:
+                            rv = LostObject, tuple()
                     else:
                         reductor = getattr(x, "__reduce__", None)
                         if reductor:
                             rv = reductor()
+                            try:
+                                x.__reduce__()
+                            except TypeError:
+                                rv = LostObject, tuple()
                         else:
                             raise Error(
                                 "un(deep)copyable object of type %s" % cls)
@@ -194,7 +209,12 @@

 _deepcopy_dispatch = d = {}

+from pickle import dumps
+class LostObject(object): pass
 def _deepcopy_atomic(x, memo):
+    try:
+        dumps(x)
+    except TypeError: return LostObject()
     return x
 d[type(None)] = _deepcopy_atomic
 d[type(Ellipsis)] = _deepcopy_atomic

Nun zurück zum Beizen Teil. Sie machen einfach ein deep diese neue deepcopy-Funktion und dann die Kopie beizen. Die unpicklable Teile während des Kopiervorgangs entfernt wurden.

x = dict(a=1)
xx = dict(x=x)
x['xx'] = xx
x['f'] = file('/tmp/1', 'w')
class List():
    def __init__(self, *args, **kwargs):
        print 'making a copy of a list'
        self.data = list(*args, **kwargs)
x['large'] = List(range(1000))
# now x contains a loop and a unpickable file object
# the following line will throw
from pickle import dumps, loads
try:
    dumps(x)
except TypeError:
    print 'yes, it throws'

def check_picklable(x):
    try:
        dumps(x)
    except TypeError:
        return False
    return True

class LostObject(object): pass

from mcopy import deepcopy

# though x has a big List object, this deepcopy will not make a new copy of it
c = deepcopy(x)
dumps(c)
cc = loads(dumps(c))
# check loop refrence
if cc['xx']['x'] == cc:
    print 'yes, loop reference is preserved'
# check unpickable part
if isinstance(cc['f'], LostObject):
    print 'unpicklable part is now an instance of LostObject'
# check large object
if loads(dumps(c))['large'].data[999] == x['large'].data[999]:
    print 'large object is ok'

Hier ist die Ausgabe:

making a copy of a list
yes, it throws
yes, loop reference is preserved
unpicklable part is now an instance of LostObject
large object is ok

Sie sehen, dass 1) die gegenseitigen Zeiger (zwischen x und xx) bleiben erhalten und wir nicht in Endlosschleife laufen; 2) das unpicklable Dateiobjekt wird eine LostObject Instanz umgewandelt wird; und 3) nicht neue Kopie des großen Objekts erstellt wird, da es picklable ist.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top