2010-10-04 15 views
10

tengo una clase que hereda de defaultdict así:Cómo eliminar y eliminar instancias de una clase que hereda de defaultdict?

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

puedo conservar en vinagre, pero cuando me unpickle, esto sucede:

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

La clase no define ningún método especial de el protocolo de pickle El decapado y el descosido de un normal defaultdict(list) funciona como se esperaba. ¿Alguien puede iluminarme?

+0

está usando 'pickle' o' cpickle'? – aaronasterling

+0

Estoy usando 'cPickle'. –

Respuesta

8

Los tipos definen cómo las apariciones se decaparán mediante la definición de uno o más de un conjunto (bastante grande) de métodos. Cada uno tiene su propio comportamiento sutil. Ver the docs on the pickle protocol. En el caso de collections.defaultdict, se utiliza el método __reduce__:

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

El primer elemento de la tupla no es el tipo, y el segundo elemento es la tupla de argumentos para pasar al tipo cuando instanciar ella. Si no anula __reduce__, el primer elemento cambiará correctamente a su tipo, pero el segundo elemento no. Esto causa el error que ves. Un crudo ejemplo de cómo se puede arreglar:

>>> 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 sólo un ejemplo crudo porque hay más de decapado (como __reduce_ex__) y todo es bastante complicado. En este caso, usar __getinitargs__ puede ser más conveniente.

Como alternativa, puede hacer que el método __init__ de su clase toma un opcional exigible, por defecto a list, o usted podría utilizar una función en lugar de una clase:

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

Gracias! Decidí ir por el parámetro predeterminado, ya que parecía la opción más simple y más estable. –

+0

¿De verdad? La función es * mucho * más simple :) –

+0

Gracias. Esto me ayudó a resolver el problema de usar '[0] * self._size' como valor predeterminado, y conseguir que el parámetro' _size' persistiera sobre el decapado. –

-1

Ese error indica que se esperaba que su clase 'listdict' tomara un argumento (el self implícito), pero obtuvo dos argumentos.

Su clase hereda de defaultdict y define un inicializador. Este inicializador llama al inicializador de defaultdict y le pasa 'list', que en este caso puede ser una función o una clase. (No me molesto en verificar)

Lo que probablemente quiere decir es que hacer esto:

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

Ahora, cuando listdict se inicializa con una lista dada, pasa esa lista al constructor de la defaultdict, en lugar de pasar una referencia a la lista global.

(Dicho esto, se considera un mal estilo usar un nombre que sea igual que los métodos y clases globales comunes, como 'str', 'list', etc., por el mismo motivo por el que se confundieron).

+1

'list' es una clase. OP no estaba pasando una lista en particular, pero la clase que, como invocable, sirve como una función de fábrica para sí misma, que es lo que el constructor de 'defaultdict' toma como argumento. tener 'listdict' take' list' como argumento frustra el propósito de la subclasificación 'defaultdict'. – aaronasterling

+0

@ Aaron tiene razón. –

+0

es inevitable que suceda de vez en cuando – aaronasterling

Cuestiones relacionadas