2010-10-04 13 views
10

Ich habe eine Klasse, die von defaultdict wie folgt erbt:Wie können Sie Instanzen einer Klasse, die von defaultdict erbt, pickle und unpickle?

class listdict(defaultdict): 
    def __init__(self): 
     defaultdict.__init__(self, list) 

Ich kann es Pickles, aber wenn ich es unpickle, dies geschieht:

('__init__() takes exactly 1 argument (2 given)', <class 'listdict'>, (<type 'list'>,)) 

Die Klasse definiert keine speziellen Methoden der das Gurkenprotokoll. Das Beizen und Entnehmen eines normalen defaultdict(list) funktioniert wie erwartet. Kann mich jemand aufklären?

+0

Sie verwenden 'pickle' oder' cpickle'? – aaronasterling

+0

Ich benutze 'cPickle'. –

Antwort

8

Typen definieren, wie Instanzen davon durch die Definition eines oder mehrerer (ziemlich großer) Methoden gebeizt werden. Jeder hat sein eigenes subtiles Verhalten. Siehe the docs on the pickle protocol. Im Fall von collections.defaultdict, verwendet es die __reduce__ Methode:

>>> l = collections.defaultdict(list) 
>>> l.__reduce__() 
(<type 'collections.defaultdict'>, (<type 'list'>,), None, None, <dictionary-itemiterator object at 0x7f031fb3c470>) 

Das erste Element in dem Tupel gibt, ist die Art und das zweite Element ist das Tupel der Argumente mit dem Typ, übergibt, wenn es instanziieren. Wenn Sie __reduce__ nicht überschreiben, wird das erste Element korrekt in Ihren Typ geändert, das zweite Element jedoch nicht. Dies verursacht den Fehler, den Sie sehen. Ein krasses Beispiel dafür, wie man es beheben könnte:

>>> import collections 
>>> import pickle 
>>> class C(collections.defaultdict): 
...  def __init__(self): 
...   collections.defaultdict.__init__(self, list) 
...  def __reduce__(self): 
...   t = collections.defaultdict.__reduce__(self) 
...   return (t[0],()) + t[2:] 
... 
>>> c = C() 
>>> c[1].append(2) 
>>> c[2].append(3) 
>>> c2 = pickle.loads(pickle.dumps(c)) 
>>> c2 == c 
True 

Es ist nur ein grobes Beispiel, weil es mehr zu Beizen (wie __reduce_ex__) und es ist alles ziemlich kompliziert. In diesem Fall kann die Verwendung von __getinitargs__ bequemer sein.

Alternativ können Sie Ihre Klasse __init__ Methode nehmen Sie eine optional aufrufbar machen, zu list säumige oder Sie könnten nur eine Funktion statt einer Klasse verwenden:

def listdict(): 
    return collections.defaultdict(list) 
+0

Danke! Ich entschied mich für den Standardparameter, da dies die einfachste und stabilste Option zu sein schien. –

+0

Wirklich? Die Funktion ist * viel * einfacher :) –

+0

Danke. Dies half mir, das Problem der Verwendung von '[0] * self._size' als Standardwert zu lösen, und das Erhalten des' _size' Parameters blieb über dem Beizen bestehen. –

-1

Dieser Fehler zeigt an, dass Ihre 'listdict' Klasse ein Argument (das implizite self) erwartet, aber zwei Argumente hat.

Ihre Klasse erbt von defaultdict und definiert einen Initialisierer. Dieser Initialisierer ruft den Initializer von defaultdict auf und übergibt ihm 'list', was in diesem Fall entweder eine Funktion oder eine Klasse sein kann. (Ich kann nicht gestört werden zu überprüfen).

Was Sie wahrscheinlich bedeutete dies zu tun:

class listdict(defaultdict): 
    def __init__(self, list): 
     defaultdict.__init__(self, list) 

Wenn nun listdict mit einer vorgegebenen Liste initialisiert, es gibt diese Liste an den Konstruktor der defaultdict, anstatt einen Verweis auf die globale Liste übergeben.

(Das heißt, es gilt als schlechter Stil, einen Namen zu verwenden, der den üblichen globalen Methoden und Klassen entspricht, wie 'str', 'list' usw. aus demselben Grund, aus dem Sie verwirrt wurden).

+1

'Liste' ist eine Klasse. OP übergab nicht eine bestimmte Liste, sondern die Klasse, die als Callable als Factory-Funktion für sich selbst dient, was der Konstruktor von defaultdict als Argument nimmt. wenn 'listdict'' 'liste' 'als Argument akzeptiert, wird der Zweck der Unterklasse' defaultdict' nicht erreicht. – aaronasterling

+0

@Aaron hat es richtig gemacht. –

+0

es wird immer wieder vorkommen – aaronasterling

Verwandte Themen