2017-09-08 33 views
4

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:

  1. Der Typ mit getattr auf dem Modul auffindbar sein muss, selbst
  2. Der Typ mit dem, was getattr Funden in Einklang stehen muss, das heißt, wenn wir pickle.dumps(o) nennen es muss dann wahr zu sein, dass type(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.loadsundump mit der gleichen Sache zu nennen)

+0

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

Antwort

1

Um f, pickle Bedürfnisse Beize f ‚s Klasse beizen, module.foo.bar.woof.last.

Die Dokumente beanspruchen keine Unterstützung für das Beizen beliebiger Klassen. They claim the following:

Folgende Typen gebeizt werden können:

  • ...
  • Klassen, die von einem Modul auf der obersten Ebene definiert ist, auf der obersten Ebene eines Moduls definiert sind

module.foo.bar.woof.last nicht, auch ein Modul vortäuschen wie module. In diesem nicht-offiziell unterstützten Fall endet die Beize Logik des Versuch module.foo.bar.woof beizen, entweder here:

elif parent is not module: 
     self.save_reduce(getattr, (parent, lastname)) 

oder here

else if (parent != module) { 
     PickleState *st = _Pickle_GetGlobalState(); 
     PyObject *reduce_value = Py_BuildValue("(O(OO))", 
            st->getattr, parent, lastname); 
     status = save_reduce(self, reduce_value, NULL); 

module.foo.bar.woof kann nicht aus mehreren Gründen gebeizt werden. Es gibt eine nicht aufrufbare Magic Instanz für alle nicht unterstützten Methoden-Lookups zurück, wie zum Beispiel __getstate__, wo der erste Fehler herkommt. Die Modulumschaltung verhindert, dass die Klasse Magic sie einbindet, und daher kommt der zweite Fehler. Es gibt wahrscheinlich mehr Inkompatibilitäten.

0

Wie es scheint, und ist bereits bewiesen, dass die Klasse machen callable nur ist eine andere falsche Richtung driften aus, zum Glück zu this hack, konnte ich eine Getaround finden die Klasse reiterable von seiner Art zu machen. nach dem Kontext des Fehlers <class 'module.Magic'>: it's not the same object as module.Magic iteriert der Abbeißer nicht den gleichen Aufruf, der einen anderen Typ von dem anderen abruft. Dies ist ein häufiges Problem beim Abbeizen von selbst Instanziierungsklassen, für dieses Beispiel ein Objekt nach seiner Klasse für die Lösung ist das Patchen der Klasse mit ihrem Typ @mock.patch('module.Magic', type(module.Magic)) Dies ist eine kurze Antwort für etwas.

Main.py

import module 
import pickle 
import mock 


f=module1.foo.bar.woof.last 
print(f().__getstate__()) # See, *this* works 
print('Pickle starts here') 
@mock.patch('module1.Magic', type(module1.Magic)) 
def pickleit(): 
    return pickle.dumps(f()) 
print(pickleit()) 

Magie Klasse

class Magic(object): 

    def __init__(self, value): 
     self.path = value 

    __class__: lambda x:x 

    def __getstate__(self): 
     print ("Shoot me! i'm at " + self.path) 
     return dump(self) 

    def __setstate__(self,value): 
     print ('something will never occur') 
     return undump(self,value) 

    def __spec__(self): 
     print ("Wrong side of the planet ") 

    def _initializing(self): 
     print ("Even farther lost ") 

    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) 
     print('terminal stage') 
     return make_type(self.path + '.' + name) 

Selbst wenn dies nicht mehr ist, den Ball von der Kante der Fledermaus von auffällig, kann ich den Inhalt sehen abgeladen in meine Konsole.