2013-01-09 8 views
9

Jeśli mam skrypt, który definiuje klasę:Jak rozpakować obiekt, którego klasa istnieje w innym obszarze nazw (python)?

script = """ 

class myClass: 
    def __init__(self): 
     self.name = 'apple' 
     self.color = 'green' 

""" 

a następnie Exec tego skryptu w swojej własnej przestrzeni nazw dict:

NS = {} 
exec script in NS 

a następnie utworzyć instancję klasy i marynowane go:

a = NS['myClass']() 

import pickle 

save = pickle.dumps(a) 

teraz, gdy próbuję go unpickle:

load = pickle.loads(save) 

pojawia się błąd

AttributeError: 'module' object has no attribute 'myClass' 

wnoszę, że to nie działa, ponieważ Python nie wie, gdzie znaleźć myClass aby odbudować obiekt. Ale myClass istnieje w dyktaturze NS. Czy istnieje sposób, aby powiedzieć pickle, gdzie znaleźć klasę dla ładowanego obiektu?

Odpowiedz

2

Odkryłem to rozwiązanie. Wygląda na to, że problem polega na tym, że wykonywanie kodu w dykcie uniemożliwia pytonowi wykrycie, gdzie zdefiniowana jest klasa. Rozwiązaniem jest utworzenie pustego modułu, wykonanie kodu w module, a następnie dodanie modułu do sys.modules, aby pyton wiedział o tym.

script = """ 
class myClass: 
    def __init__(self): 
     self.name = 'apple' 
     self.color = 'green' 
""" 

import imp, sys 

moduleName = 'custom' 

module = imp.new_module(moduleName) 

exec script in module.__dict__ 

sys.modules[moduleName] = module 

Teraz możliwe jest marynowane i unpickle instancję klasy:

import pickle 
a = module.myClass() 
s = pickle.dumps(a) 
b = pickle.loads(s) 
3

Możesz pójść o krok dalej, a obiekt może się zrekonstruować na dowolny typ.

import pickle 
import copy_reg 

class myClass(object): 
    def __init__(self): 
     self.apple = 'banana' 

class otherclass(object): 
    def __init__(self): 
     self.apple = 'existential woe' 

def pickle_an_object(o): 
    print "pickling %s" % str(o) 
    return otherclass, (o.apple,) 

copy_reg.pickle(myClass, pickle_an_object) 

foo = myClass() 

s = pickle.dumps(foo) 

del myClass 
del otherclass 

class otherclass(object): 
    def __init__(self, appletype): 
     self.apple = 'not %s' % appletype 

o2 = pickle.loads(s) 

print o2.apple 

Podstawowym założeniem jest to, że spakować klasę w „konia trojańskiego” sortuje, gdzie jego rekonstrukcja powodującej konkretyzacji innej klasy od tego, co pierwotnie było.

To nie ma znaczenia co zawiera otherclass po stronie wytrawiania. Liczy się tylko to, że istnieje na tej samej ścieżce modułu, co klasa "docelowa" - pickle to właśnie umieszczanie w strumieniu serializowanym reprezentacji łańcuchowej nazwy modułu.

Tak więc, aby rozbić, co dzieje się w powyższym kodzie w szczegółach:

  • Rejestrujemy niestandardową Pickler dla myClass. Można to zrobić za pomocą funkcji copy_reg lub __reduce_ex__.
  • Nasz niestandardowy Pickler mówi „marynowane to jako przykład otherclass” (które jest obojętne. Ty nie potrzeba „prawdziwego” zawartość otherclass na stronie wytrawiania, ponieważ wszystko, co przechodzi w zalewie jest moduł /Nazwa klasy).
  • Pobieramy obiekt i "wysyłamy go przez kabel", tam gdzie istnieje prawdziwa wersja otherclass.
  • Po stronie zdalnej utworzono instancję otherclass z danymi z krotki zwróconej przez niestandardową funkcję wytrawiania.

Python może być całkiem potężny!

+0

To wszystko jest bardzo zadbane, ale jak to odebrać oryginalne pytanie? –

+1

@JohnY Możesz sprawić, by klasa została rozpakowana w instancję dowolnej klasy z przestrzeni nazw rozpakowacza. Ustawiasz 'myClass', aby rozpakować jako' foomodule.myClass', a następnie wypowiadasz 'foomodule.myClass = NS ['myClass']' przed wywołaniem 'pickle.loads'. – Borealid

Powiązane problemy