python - functions - dict.fromkeys apuntan a la misma lista
dictionary method python (4)
Esto me estaba causando un poco de dolor ...
Creé un diccionario de una lista
l = [''a'',''b'',''c'']
d = dict.fromkeys(l, [0,0]) # initializing dictionary with [0,0] as values
d[''a''] is d[''b''] # returns True
¿Cómo puedo hacer que cada valor del diccionario sea una lista separada? ¿Es esto posible sin iterar sobre todas las teclas y establecerlas igual a una lista? Me gustaría modificar una lista sin cambiar todas las demás.
Esta respuesta está aquí para explicar este comportamiento a cualquier persona desconcertada por los resultados que obtienen de intentar crear una instancia de un dict
con fromkeys()
con un valor predeterminado mutable en ese 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
entonces cualquier cambio a l1
no afectará a l2
y viceversa. esto sería cierto para cualquier mutable hasta el momento, incluida una dict
.
# create a new dict from an iterable of keys
>>> dict1 = dict.fromkeys([''a'', ''b'', ''c''], [])
>>> dict1
{''c'': [], ''b'': [], ''a'': []}
esto puede ser una función útil. aquí estamos asignando a cada tecla 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 usan la misma referencia. ¡Un cambio a uno es un cambio para todos, ya que de hecho 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 usa como el valor predeterminado.
>>> empty_list = []
>>> id(empty_list)
140150324169864
y ahora crea un dict 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! Escuché a alguien gritar, ¡es porque utilicé 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 None
al 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 los mismos (¡y el único!) None
. Ahora, iteremos, en una de una miríada de formas, a través del dict
y cambiemos el valor.
>>> for k, _ in dict4.items():
... dict4[k] = []
>>> dict4
{''c'': [], ''b'': [], ''a'': []}
Hmm. ¡Parece lo mismo 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 []
s, que en este caso fue el resultado esperado.
Puedes usar esto:
l = [''a'', ''b'', ''c'']
d = dict((k, [0, 0]) for k in l)
Puedes usar una comprensión dict:
>>> keys = [''a'',''b'',''c'']
>>> value = [0, 0]
>>> {key: list(value) for key in keys}
{''a'': [0, 0], ''b'': [0, 0], ''c'': [0, 0]}
l = [''a'',''b'',''c'']
d = dict((i, [0, 0]) for i in l)