¿Cómo inicializo un diccionario de listas vacías en Python?

88

Mi intento de crear un diccionario de listas mediante programación no me permite abordar las claves del diccionario individualmente. Siempre que creo el diccionario de listas e intento agregar una clave, todas se actualizan. Aquí hay un caso de prueba muy simple:

data = {}
data = data.fromkeys(range(2),[])
data[1].append('hello')
print data

Resultado actual: {0: ['hello'], 1: ['hello']}

Resultado Esperado: {0: [], 1: ['hello']}

Esto es lo que funciona

data = {0:[],1:[]}
data[1].append('hello')
print data

Resultado real y esperado: {0: [], 1: ['hello']}

¿Por qué el fromkeysmétodo no funciona como se esperaba?

Martin Burch
fuente

Respuestas:

111

Pasar []como segundo argumento a dict.fromkeys()da un resultado bastante inútil: todos los valores en el diccionario serán el mismo objeto de lista.

En Python 2.7 o superior, puede usar una comprensión dicitonaria en su lugar:

data = {k: [] for k in range(2)}

En versiones anteriores de Python, puede usar

data = dict((k, []) for k in range(2))
Sven Marnach
fuente
3
Bueno, este es un comportamiento bastante poco intuitivo, ¿alguna idea de por qué se usa el mismo objeto para todas las claves?
Bar
2
@Bar Porque no hay nada más que la función pueda hacer dentro de la semántica del lenguaje Python. Se pasa un solo objeto para que se use como valor para todas las claves, de modo que se use un solo objeto para todas las claves. Sería mejor que el fromkeys()método aceptara una función de fábrica en su lugar, por lo que podríamos pasar listcomo una función, y esa función se llamaría una vez por cada clave creada, pero esa no es la API real de dict.fromkeys().
Sven Marnach
3
Esto no es nada intuitivo. Me tomó una hora encontrarlo. Gracias
Astrid
1
Lo mismo sucede si pasa dict () como segundo argumento. Comportamiento muy desconcertante.
Orly
@Orly Esto se debe a que primero se crea un diccionario vacío y luego se pasa una referencia a todas las inicializaciones.
Dr_Zaszuś
83

Utilice defaultdict en su lugar:

from collections import defaultdict
data = defaultdict(list)
data[1].append('hello')

De esta forma, no es necesario que inicialice todas las claves que desea utilizar en las listas de antemano.

Lo que está sucediendo en su ejemplo es que usa una lista (mutable):

alist = [1]
data = dict.fromkeys(range(2), alist)
alist.append(2)
print data

saldría {0: [1, 2], 1: [1, 2]}.

Martijn Pieters
fuente
2
En mi caso, necesito inicializar todas las claves de antemano para que el resto de la lógica del programa pueda funcionar como se esperaba, pero esta sería una buena solución de lo contrario. Gracias.
Martin Burch
Supongo que lo que falta en esta respuesta es decir que esta solución funciona, a diferencia de la del OP, porque listaquí no hay una lista vacía, sino un tipo (o puede verlo como un constructor invocable, supongo). Entonces, cada vez que se pasa una clave faltante, se crea una nueva lista en lugar de reutilizar la misma.
Dr_Zaszuś
8

Está completando sus diccionarios con referencias a una sola lista, por lo que cuando la actualiza, la actualización se refleja en todas las referencias. En su lugar, pruebe con la comprensión del diccionario. Consulte Crear un diccionario con comprensión de listas en Python.

d = {k : v for k in blah blah blah}
cobie
fuente
gran sugerencia sobre la inicialización de los valores del diccionario ... ¡gracias cobie! Extendí su ejemplo para restablecer los valores en un diccionario existente, d. Hice esto de la siguiente manera: d = {k: 0 para k en d}
Juan
¿Qué hay ven esta respuesta?
Dr_Zaszuś
-2

Podrías usar esto:

data[:1] = ['hello']
Conner Dassen
fuente
2
Podría ser útil para el OP explicar por qué funciona esto. La pregunta original publicada pregunta por qué no funciona como se esperaba.
william.taylor.09
@ william.taylor.09 es un poco obvio por qué esto funciona, ¿no?
Conner Dassen
OP está preguntando "¿Por qué el método fromkeys no funciona como se esperaba?"
william.taylor.09