¿Cómo decapar y desmarcar instancias de una clase que hereda de defaultdict?

Tengo una clase que hereda de defaultdict como esto:

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

Puedo decaparlo, pero cuando lo desencojo, esto sucede:

 ('__init__() takes exactly 1 argument (2 given)', , (,)) 

La clase no define ningún método especial del protocolo pickle. El decapado y descifrado de un defecto predeterminado defaultdict(list) funciona como se espera. ¿Alguien me puede iluminar?

Los tipos definen cómo se declinan los casos definiendo uno o más de un conjunto de métodos (bastante grande). Cada uno tiene su propio comportamiento sutil. Ver los documentos en el protocolo de pickle . En el caso de collections.defaultdict , utiliza el método __reduce__ :

 >>> l = collections.defaultdict(list) >>> l.__reduce__() (, (,), None, None, ) 

El primer elemento de la tupla es el tipo, y el segundo elemento es la tupla de argumentos para pasar al tipo al crear una instancia. Si no invalida __reduce__ , el primer elemento cambiará correctamente a su tipo, pero el segundo no lo hará. Esto causa el error que ves. Un ejemplo crudo de cómo podría arreglarlo:

 >>> 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 solo un ejemplo __reduce_ex__ porque hay mucho más para decapar (como __reduce_ex__ ) y todo es bastante complejo. En este caso, usar __getinitargs__ puede ser más conveniente.

Alternativamente, podría hacer que el método __init__ su clase tome una opción de __init__ opcional , la list __init__ , o simplemente podría usar una función en lugar de una clase:

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

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

Su clase hereda de defaultdict y define un inicializador. Este inicializador llama al inicializador defaultdict y le pasa una “lista”, que en este caso puede ser una función o una clase. (No puedo ser molestado en comprobar).

Lo que probablemente quisiste decir es 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 del defaultdict, en lugar de pasar una referencia a la lista global.

(Dicho esto, se considera un estilo malo usar un nombre que sea igual a los métodos y clases globales comunes, como ‘str’, ‘lista’, etc., por el mismo motivo por el que se confundió).