2010-10-04 3 views
10

Ho una classe che eredita da defaultdict simili:Come decapitare e annullare le istanze di una classe che eredita da defaultdict?

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

posso decapare, ma quando deserializzare esso, questo accade:

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

La classe non definisce i metodi speciali di il protocollo sottaceto. Decapaggio e annullamento di un normale defaultdict(list) funziona come previsto. Qualcuno può illuminarmi?

+0

stai usando 'pickle' o' cpickle'? – aaronasterling

+0

Sto usando 'cPickle'. –

risposta

8

I tipi definiscono il modo in cui le istanze vengono decapitate definendo uno o più di un (abbastanza grande) insieme di metodi. Ognuno ha il suo comportamento sottile. Vedi the docs on the pickle protocol. Nel caso di collections.defaultdict, utilizza il metodo __reduce__:

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

Il primo elemento della tupla c'è il tipo, e il secondo elemento è tupla di argomenti per passare al tipo quando istanziandola. Se non si esegue l'override di __reduce__, il primo elemento verrà modificato correttamente nel tipo, ma il secondo non lo farà. Ciò causa l'errore che vedi. Un esempio di greggio di come si potrebbe risolvere il problema:

>>> 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 

E 'solo un esempio di greggio perché non c'è più per decapaggio (come __reduce_ex__) ed è tutto abbastanza intricata. In questo caso, usare __getinitargs__ potrebbe essere più conveniente.

In alternativa, si potrebbe fare il metodo __init__ di vostra classe prendere un opzionale callable, inadempiente a list, o si potrebbe utilizzare una funzione invece di una classe:

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

Grazie! Ho deciso di utilizzare il parametro predefinito, in quanto sembrava l'opzione più semplice e più stabile. –

+0

Davvero? La funzione è * molto * più semplice :) –

+0

Grazie. Questo mi ha aiutato a risolvere il problema dell'uso di '[0] * self._size' come valore predefinito, e il parametro' _size' persisteva sul pickling. –

-1

Questo errore indica che la classe 'listdict' avrebbe dovuto prendere un argomento (il sé implicito), ma ha ottenuto due argomenti.

La classe eredita da defaultdict e definisce un inizializzatore. Questo inizializzatore richiama l'inizializzatore di defaultdict e lo passa a "lista", che in questo caso può essere una funzione o una classe. (Non posso essere preso la briga di controllare).

Ciò che probabilmente si intende è di fare questo:

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

Ora, quando listdict viene inizializzato con una data lista, si passa tale elenco al costruttore della defaultdict, invece di passare un riferimento alla lista globale.

(Ciò detto, è considerato di cattivo stile utilizzare un nome uguale ai comuni metodi e classi globali, come "str", "elenco", ecc., Per lo stesso motivo per cui si è confusi).

+1

'list' è una classe. L'OP non stava passando una lista particolare ma la classe che, come un callable, funge da funzione di fabbrica per se stessa, che è ciò che il costruttore di 'defaultdict' prende come argomento. avere 'listdict' usa' list' come argomento per sconfiggere lo scopo della sottoclasse 'defaultdict'. – aaronasterling

+0

@Aaron ha ragione. –

+0

è destinato a succedere ogni tanto – aaronasterling