Ich habe versucht, einige dynamisch erstellte Typen (d. H. Diejenigen, die durch Aufruf von 3-Arg type()
erstellt wurden) zu pickle und unpickle schön zu bekommen. Ich habe this module switching trick verwendet, um die Details von Benutzern des Moduls zu verbergen und saubere Semantik zu geben.Beizen dynamisch erstellte Typen
Ich habe schon ein paar Dinge gelernt:
- Der Typ mit
getattr
auf dem Modul auffindbar sein muss, selbst - Der Typ mit dem, was
getattr
Funden in Einklang stehen muss, das heißt, wenn wirpickle.dumps(o)
nennen es muss dann wahr zu sein, dasstype(o) == getattr(module, 'name of type')
Wo ich aber bin stecken ist, dass es noch etwas seltsam los zu sein scheint - es scheint, __getstate__
Aufruf zu auf etwas Unerwartetes.
Hier ist es am einfachsten, die ich habe, das das Problem reproduziert, mit Python zu testen 3.5, aber ich mag auf 3,3 Ziel zurück, wenn möglich:
# module.py
import sys
import functools
def dump(self):
return b'Some data' # Dummy for testing
def undump(self, data):
print('Undump: %r' % data) # Do nothing for testing
# Cheaty demo way to make this consistent
@functools.lru_cache(maxsize=None)
def make_type(name):
return type(name,(), {
'__getstate__': dump,
'__setstate__': undump,
})
class Magic(object):
def __init__(self, path):
self.path = path
def __getattr__(self, name):
print('Getting thing: %s (from: %s)' % (name, self.path))
# for simple testing all calls to make_type must end in last x.y.z.last
if name != 'last':
if self.path:
return Magic(self.path + '.' + name)
else:
return Magic(name)
return make_type(self.path + '.' + name)
# Make the switch
sys.modules[__name__] = Magic('')
Und dann eine schnelle Möglichkeit, dass die Ausübung :
import module
import pickle
f=module.foo.bar.woof.last()
print(f.__getstate__()) # See, *this* works
print('Pickle starts here')
print(pickle.dumps(f))
die dann ergibt sich:
Getting thing: foo (from:)
Getting thing: bar (from: foo)
Getting thing: woof (from: foo.bar)
Getting thing: last (from: foo.bar.woof)
b'Some data'
Pickle starts here
Getting thing: __spec__ (from:)
Getting thing: _initializing (from: __spec__)
Getting thing: foo (from:)
Getting thing: bar (from: foo)
Getting thing: woof (from: foo.bar)
Getting thing: last (from: foo.bar.woof)
Getting thing: __getstate__ (from: foo.bar.woof)
Traceback (most recent call last):
File "test.py", line 7, in <module>
print(pickle.dumps(f))
TypeError: 'Magic' object is not callable
ich nichts sehen nicht erwartet bissuchenauf module.foo.bar.woof
, aber selbst wenn wir diese Lookup durch fehlschlagen zwingen Zugabe:
if name == '__getstate__': raise AttributeError()
in unsere __getattr__
es immer noch nicht mit:
Traceback (most recent call last):
File "test.py", line 7, in <module>
print(pickle.dumps(f))
_pickle.PicklingError: Can't pickle <class 'module.Magic'>: it's not the same object as module.Magic
Was soll das? Fehle ich etwas mit __spec__
? Die docs for __spec__
ziemlich genau betonen es angemessen, aber scheinen nicht wirklich viel zu erklären.
Noch wichtiger ist die größere Frage, wie soll ich gehen über die Herstellung von Typen, die ich programmatisch über __getattr__
Implementierungsgurke eines Pseudo-Moduls generiert richtig?
(Und natürlich, wenn ich es geschafft haben pickle.dumps
zu bekommen, etwas zu produzieren erwarte ich pickle.loads
undump
mit der gleichen Sache zu nennen)
Gibt es einen Grund, warum Sie den Modulschalttrick verwenden müssen? Ich habe Ihr Beispiel ein wenig vereinfacht und es funktioniert gut mit 'module.Magic (''). Foo.bar.woof.last()'. Der Modulschalter verursacht den Fehler "Es ist nicht das gleiche Objekt". Ich arbeite immer noch an den Details, was all diese Probleme verursacht. – illiteratecoder