2011-01-13 4 views
29

Sto cercando di fare la serializzazione un namedtuple:Python: Impossibile salamoia tipo X, attributo di ricerca fallita

from collections import namedtuple 
import cPickle 

class Foo: 

    Bar = namedtuple('Bar', ['x', 'y']) 

    def baz(self): 
     s = set() 
     s.add(Foo.Bar(x=2, y=3)) 
     print cPickle.dumps(s) 

if __name__ == '__main__': 
    f = Foo() 
    f.baz() 

Questo produce il seguente output:

Traceback (most recent call last): 
    File "scratch.py", line 15, in <module> 
    f.baz() 
    File "scratch.py", line 11, in baz 
    print cPickle.dumps(s) 
cPickle.PicklingError: Can't pickle <class '__main__.Bar'>: attribute lookup __main__.Bar failed 

Che cosa sto facendo di sbagliato? Il problema è che Bar è un membro di Foo? (Spostare la definizione di Bar al livello superiore risolve il problema, anche se io sono ancora curioso di sapere perchè questo accade.)

+4

Utilizzando python3 e sottaceto protocollo 4 fissa questo –

risposta

26

Sì, il fatto che si tratta di un membro della classe è un problema:

>>> class Foo(): 
...  Bar = namedtuple('Bar', ['x','y']) 
...  def baz(self): 
...   b = Foo.Bar(x=2, y=3) 
...   print(type(b)) 
... 
>>> a = Foo() 
>>> a.baz() 
<class '__main__.Bar'> 

Il il problema è che quando namedtuple() restituisce un oggetto tipo, non è a conoscenza del fatto che è assegnato a un membro della classe - e quindi dice all'oggetto tipo che il suo nome tipo deve essere __main__.Bar, anche se dovrebbe essere davvero __main__.Foo.Bar .

+2

Quindi ... c'è qualche soluzione, o è solo proibito? –

+0

'namedtuple' non funziona bene con le classi. Potresti essere in grado di scrivere una '__getstate__' personalizzata per l'oggetto che si prenderà cura di esso manualmente. – Amber

+3

http://docs.python.org/library/pickle.html#object.__getstate__ – Amber

11

Le classi di nidificazione rendono fallito il pickle, poiché si basa sul percorso dell'oggetto all'interno dell'applicazione per ricostruirlo in un secondo momento.

La soluzione immediata è quella di non annidare le classi, ovvero spostare la definizione Bar all'esterno Foo. Il codice funzionerà lo stesso.

Ma una cosa migliore da fare è non utilizzarepickle affatto per memorizzare i dati. Utilizza un altro formato di serializzazione, ad esempio json o un database, ad esempio sqlite3.

Hai appena colpito uno dei numerosi inconvenienti di pickle, se si modifica il codice, si spostano le cose o si apportano piccole modifiche strutturali, i dati diventano scaricabili.

Oltre a ciò, salamoia ha altri svantaggi: E 'lento, non protetta, python-solo ...

3

Utilizzando aneto in luogo di salamoia qui permetterà questo lavoro