2013-05-04 9 views
35

Ich lerne, wie Gurke zu verwenden ist. Ich habe ein namedtuple-Objekt erstellt, es an eine Liste angehängt und versucht, diese Liste zu markieren. Allerdings bekomme ich folgende Fehlermeldung:Wie man eine namedtuple Instanz richtig pickelfrei macht

pickle.PicklingError: Can't pickle <class '__main__.P'>: it's not found as __main__.P 

Ich fand, dass, wenn ich den Code lief ohne es in einer Funktion Einwickeln, es funktioniert perfekt. Ist ein zusätzlicher Schritt erforderlich, um ein Objekt zu stechen, wenn es in eine Funktion eingeschlossen wird?

Hier ist mein Code:

from collections import namedtuple 
import pickle 

def pickle_test(): 
    P = namedtuple("P", "one two three four") 
    my_list = [] 
    abe = P("abraham", "lincoln", "vampire", "hunter") 
    my_list.append(abe) 
    f = open('abe.pickle', 'w') 
    pickle.dump(abe, f) 
    f.close() 

pickle_test() 
+0

Leider scheint Gurke mit Nametuples nicht gut zu funktionieren. – Antimony

+1

@Antimony: 'Pickle' behandelt benannteTuple-Klassen ganz gut; Klassen in einem lokalen Namespace Funktion nicht so sehr definiert. –

+0

mögliche Duplikate von [Python: Kann Typ X nicht picken, Attributsuche fehlgeschlagen] (http://stackoverflow.com/questions/4677012/python-cant-pickle-type-x-attribute-lookup-failed) – Air

Antwort

51

die benannte Tupel erstellen außerhalb der Funktion:

from collections import namedtuple 
import pickle 

P = namedtuple("P", "one two three four") 

def pickle_test(): 
    my_list = [] 
    abe = P("abraham", "lincoln", "vampire", "hunter") 
    my_list.append(abe) 
    f = open('abe.pickle', 'w') 
    pickle.dump(abe, f) 
    f.close() 

pickle_test() 

pickle kann es jetzt finden; Es ist jetzt ein globales Modul. Beim Entspinnen muss das pickle Modul __main__.P erneut suchen. In Ihrer Version ist P eine lokale, zu der pickle_test() Funktion, und das ist nicht inspectable oder importierbar.

Es ist wichtig, sich daran zu erinnern, dass namedtuple() eine Klassenfabrik ist; Sie geben ihm Parameter und es gibt ein Klassenobjekt zurück, aus dem Sie Instanzen erstellen können. pickle speichert nur die in den Instanzen enthaltenen Daten sowie einen String-Verweis auf die ursprüngliche Klasse, um die Instanzen erneut zu rekonstruieren.

+0

Ausgezeichnet! Vielen Dank. –

+4

Was also, wenn ich das namedtuple dynamisch erstelle, weil ich die Felder bis zur Laufzeit nicht kenne? Gibt es noch einen Weg, dieses Problem zu umgehen? Ich habe versucht, eine andere Methode außerhalb der Klasse zu erstellen, aber das hat nicht funktioniert. – Chuim

+4

@Chuim: Weisen Sie es Ihrem Modul globals zu (verwenden Sie 'globals()', um ein Mapping zu erhalten) unter dem gleichen Namen *, und 'pickle' kann es noch finden. –

5

Nachdem ich meine Frage als Kommentar zur Hauptantwort hinzugefügt habe, habe ich einen Weg gefunden, das Problem zu lösen, einen dynamisch erzeugten namedtuple Pickle-fähig zu machen. Dies ist in meinem Fall erforderlich, weil ich die Felder zur Laufzeit (nach einer DB-Abfrage) entdecke.

Alles, was ich tun ist Affe Patch die namedtuple durch effektiv an die __main__ Modul bewegt:

def _CreateNamedOnMain(*args): 
    import __main__ 
    namedtupleClass = collections.namedtuple(*args) 
    setattr(__main__, namedtupleClass.__name__, namedtupleClass) 
    namedtupleClass.__module__ = "__main__" 
    return namedtupleClass 

Durch dessen bewusst, dass die namedtuple Name (die von args vorgesehen ist) könnte ein anderes Mitglied in __main__ überschreiben, wenn du bist nicht vorsichtig.

+11

Setzen Sie es stattdessen einfach auf 'globals()': 'globals() [namedtupleClass .__ name__] = namedtupleClass'. Dann gibt es * keine Notwendigkeit * das '__module__' zu setzen. –

+0

Als ich 'globals() versuchte [namedtupleClass .__ name__] = namedtupleClass' erlaubte es mir tatsächlich, mein Objekt zu picken, aber als ich versuchte es zu entpacken, hatte es nicht die' namedtupleClass' die es benötigte. Mein Rat ist, ** benutzen Sie einfach ein Wörterbuch **, bis sie Essiggurke klug genug machen, um dies zu tun. – Teque5

2

Alternativ können Sie cloudpickle oder dill für die Serialisierung verwenden:

from collections import namedtuple 

import cloudpickle 
import dill 



def dill_test(dynamic_names): 
    P = namedtuple('P', dynamic_names) 
    my_list = [] 
    abe = P("abraham", "lincoln", "vampire", "hunter") 
    my_list.append(abe) 
    with open('deleteme.cloudpickle', 'wb') as f: 
     cloudpickle.dump(abe, f) 
    with open('deleteme.dill', 'wb') as f: 
     dill.dump(abe, f) 


dill_test("one two three four") 
1

I this answer in einem anderen Thread gefunden. Hier geht es um die Benennung des benannten Tupels. Dies funktionierte für mich:

group_t =   namedtuple('group_t', 'field1, field2') # this will work 
mismatched_group_t = namedtuple('group_t', 'field1, field2') # this will throw the error 
Verwandte Themen