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

Resuelto Michael Johnston asked hace 12 años • 0 respuestas

Mi intento de crear mediante programación un diccionario de listas no me permite abordar individualmente las claves del diccionario. Cada vez que creo el diccionario de listas e intento agregarlo a 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?

Michael Johnston avatar Jul 17 '12 00:07 Michael Johnston
Aceptado

Cuando []se pasa como segundo argumento a dict.fromkeys(), todos los valores del resultado dictserán el mismo list objeto.

En Python 2.7 o superior, utilice un diccionario de comprensión en su lugar:

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

En versiones anteriores de Python, no hay comprensión de dict, pero dicten su lugar se puede pasar una comprensión de lista al constructor:

data = dict([(k, []) for k in range(2)])

En 2.4-2.6, también es posible pasar una expresión generadora dicty se pueden eliminar los paréntesis circundantes :

data = dict((k, []) for k in range(2))
Sven Marnach avatar Jul 16 '2012 17:07 Sven Marnach

Intente usar un defaultdict en su lugar:

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

De esta manera, no es necesario inicializar las claves con listas vacías de antemano. En cambio, el defaultdict()objeto llama a la función de fábrica que se le asigna cada vez que se accede a una clave que aún no existe. Entonces, en este ejemplo, intentar acceder data[1]a los activadores data[1] = list()internamente, dándole a esa clave una nueva lista vacía como valor.

El código original .fromkeyscomparte una lista (mutable). Similarmente,

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

daría salida {0: [1, 2], 1: [1, 2]}. Esto se indica en la dict.fromkeys()documentación :

Todos los valores se refieren a una sola instancia, por lo que generalmente no tiene sentido que el valor sea un objeto mutable, como una lista vacía.

Otra opción es usar el dict.setdefault()método , que recupera el valor de una clave después de verificar primero que existe y establecer un valor predeterminado si no es así. .appendLuego se puede invocar según el resultado:

data = {}
data.setdefault(1, []).append('hello')

Finalmente, para crear un diccionario a partir de una lista de claves conocidas y una lista de "plantilla" determinada (donde cada valor debe comenzar con los mismos elementos, pero ser una lista distinta), use un diccionario por comprensión y copie la lista inicial:

alist = [1]
data = {key: alist[:] for key in range(2)}

Aquí, alist[:]se crea una copia superficial de alisty esto se hace por separado para cada valor. Consulte ¿Cómo clono una lista para que no cambie inesperadamente después de la asignación? para obtener más técnicas para copiar la lista.

Martijn Pieters avatar Jul 16 '2012 17:07 Martijn Pieters

Podrías usar un dict de comprensión:

>>> keys = ['a','b','c']
>>> value = [0, 0]
>>> {key: list(value) for key in keys}
    {'a': [0, 0], 'b': [0, 0], 'c': [0, 0]}
Blender avatar Mar 20 '2013 06:03 Blender

Esta respuesta está aquí para explicar este comportamiento a cualquiera que esté desconcertado por los resultados que obtiene al intentar crear una instancia de dictwith fromkeys()con un valor predeterminado mutable en that dict.

Considerar:

#Python 3.4.3 (default, Nov 17 2016, 01:08:31) 

# start by validating that different variables pointing to an
# empty mutable are indeed different references.
>>> l1 = []
>>> l2 = []
>>> id(l1)
140150323815176
>>> id(l2)
140150324024968

por lo que cualquier cambio l1no afectará l2y viceversa. Esto sería cierto para cualquier mutable hasta ahora, incluido un dict.

# create a new dict from an iterable of keys
>>> dict1 = dict.fromkeys(['a', 'b', 'c'], [])
>>> dict1
{'c': [], 'b': [], 'a': []}

esta puede ser una función útil. aquí asignamos a cada clave un valor predeterminado que también resulta ser una lista vacía.

# the dict has its own id.
>>> id(dict1)
140150327601160

# but look at the ids of the values.
>>> id(dict1['a'])
140150323816328
>>> id(dict1['b'])
140150323816328
>>> id(dict1['c'])
140150323816328

De hecho, ¡todos están usando la misma referencia! ¡Un cambio en uno es un cambio en todos, ya que en realidad son el mismo objeto!

>>> dict1['a'].append('apples')
>>> dict1
{'c': ['apples'], 'b': ['apples'], 'a': ['apples']}
>>> id(dict1['a'])
>>> 140150323816328
>>> id(dict1['b'])
140150323816328
>>> id(dict1['c'])
140150323816328

¡Para muchos, esto no era lo que se pretendía!

Ahora intentémoslo haciendo una copia explícita de la lista que se utiliza como valor predeterminado.

>>> empty_list = []
>>> id(empty_list)
140150324169864

y ahora crea un dictado con una copia de empty_list.

>>> dict2 = dict.fromkeys(['a', 'b', 'c'], empty_list[:])
>>> id(dict2)
140150323831432
>>> id(dict2['a'])
140150327184328
>>> id(dict2['b'])
140150327184328
>>> id(dict2['c'])
140150327184328
>>> dict2['a'].append('apples')
>>> dict2
{'c': ['apples'], 'b': ['apples'], 'a': ['apples']}

¡Todavía no hay alegría! Escucho a alguien gritar, ¡es porque usé una lista vacía!

>>> not_empty_list = [0]
>>> dict3 = dict.fromkeys(['a', 'b', 'c'], not_empty_list[:])
>>> dict3
{'c': [0], 'b': [0], 'a': [0]}
>>> dict3['a'].append('apples')
>>> dict3
{'c': [0, 'apples'], 'b': [0, 'apples'], 'a': [0, 'apples']}

El comportamiento predeterminado de fromkeys()es asignar Noneel valor.

>>> dict4 = dict.fromkeys(['a', 'b', 'c'])
>>> dict4
{'c': None, 'b': None, 'a': None}
>>> id(dict4['a'])
9901984
>>> id(dict4['b'])
9901984
>>> id(dict4['c'])
9901984

De hecho, todos los valores son iguales (¡y los únicos!) None. Ahora, repitamos, de una infinidad de maneras, a través de dicty cambiemos el valor.

>>> for k, _ in dict4.items():
...    dict4[k] = []

>>> dict4
{'c': [], 'b': [], 'a': []}

Mmm. ¡Se ve igual que antes!

>>> id(dict4['a'])
140150318876488
>>> id(dict4['b'])
140150324122824
>>> id(dict4['c'])
140150294277576
>>> dict4['a'].append('apples')
>>> dict4
>>> {'c': [], 'b': [], 'a': ['apples']}

Pero en realidad son diferentes [], que en este caso era el resultado deseado.

Shawn Mehan avatar Jun 12 '2017 18:06 Shawn Mehan

Puedes usar esto:

l = ['a', 'b', 'c']
d = dict((k, [0, 0]) for k in l)
g.d.d.c avatar Mar 20 '2013 06:03 g.d.d.c