Estoy tratando de agrupar las cadenas binarias de ciertos números en función de cuántos 1 hay en la cadena.
Esto no funciona
s = "0 1 3 7 8 9 11 15"
numbers = map(int, s.split())
binaries = [bin(x)[2:].rjust(4, '0') for x in numbers]
one_groups = dict.fromkeys(range(5), [])
for x in binaries:
one_groups[x.count('1')] += [x]
El diccionario esperado one_groups
debe ser
{0: ['0000'],
1: ['0001', '1000'],
2: ['0011', '1001'],
3: ['0111', '1011'],
4: ['1111']}
Pero consigo
{0: ['0000', '0001', '0011', '0111', '1000', '1001', '1011', '1111'],
1: ['0000', '0001', '0011', '0111', '1000', '1001', '1011', '1111'],
2: ['0000', '0001', '0011', '0111', '1000', '1001', '1011', '1111'],
3: ['0000', '0001', '0011', '0111', '1000', '1001', '1011', '1111'],
4: ['0000', '0001', '0011', '0111', '1000', '1001', '1011', '1111']}
Hasta ahora, lo único que ha funcionado es si uso en one_groups[x.count('1')] = one_groups.get(x.count('1')) + [x]
lugar deone_groups[x.count('1')] += [x]
¿Pero por qué es así? Si recuerdo correctamente, ¿no se dict[key]
supone que devuelve el valor de ese diccionario, similar a cómo dict.get(key)
funciona? He visto este hilo ¿Por qué dict.get (key) en lugar de dict [key]? pero no respondió mi pregunta para este caso en particular, ya que estoy seguro de que el programa no está destinado a obtener elKeyError
También lo he intentado one_groups[x.count('1')].append(x)
pero esto tampoco funciona.
fuente
get
devuelveNone
si la clave no existe o algún valor predeterminado proporcionado, mientras que el operador de índice[]
genera un error si la clave no existe.bin(x)[2:].rjust(4, '0')
se puede simplificar a'{:0>4b}'.format(x)
.binaries
no es relevante para la pregunta, por lo que podría proporcionar su valor.Respuestas:
El problema es la mutabilidad:
one_groups = dict.fromkeys(range(5), [])
- esto pasa la misma lista como valor a todas las claves . Entonces, si cambia un valor, los cambia a todos.Es básicamente lo mismo que decir:
Si desea utilizar una nueva lista, debe hacerlo en un bucle, ya sea un
for
bucle explícito o en una comprensión dict:Esta cosa se "ejecutará"
[]
(lo que equivale alist()
) para cada clave, haciendo así los valores con diferentes listas.¿Por qué
get
funciona? Porque explícitamente toma la lista actual, pero+
crea una nueva lista de resultados. Y no importa si esone_groups[x.count('1')] = one_groups.get(x.count('1')) + [x]
oone_groups[x.count('1')] = one_groups[x.count('1')] + [x]
lo que importa es que hay+
.Sé que todo el mundo dice que
a+=b
es justoa=a+b
, pero la implementación puede ser diferente para la optimización; en el caso de las listas,+=
es solo.extend
porque sabemos que queremos nuestro resultado en la variable actual, por lo que crear una nueva lista sería un desperdicio de memoria.fuente
mylist = [[] * 5] * 5
y cómo lomylist = [[] for x in range(5)] * 5
habría solucionado. Solo para una aclaración rápida, según entendí, esto sucede debido a las variables que apuntan a la dirección de memoria de esa lista vacía. ¿Esto también significa que el problema no ocurriría si usara primitivas en su lugar?one_groups[x.count('1')] += [x]
porque no puede agregar una lista a un tipo primitivo. Una mejor solución es usar defaultdict en su lugar.+
llama__add__
y devuelve un nuevo objeto, mientras que+=
llama__iadd__
, y no está obligado a devolver un nuevo objetoEl problema es usar
one_groups = dict.fromkeys(range(5), [])
(Esto pasa la misma lista como valor a todas las claves. Entonces, si cambia un valor, los cambia a todos)
Puedes usar esto en su lugar:
one_groups = {i:[] for i in range(5)}
(Esta cosa "ejecutará" [] (que equivale a list ()) para cada clave, haciendo así los valores con diferentes listas.)
fuente
Esta es la ayuda sobre el
fromkeys
método de dict .Eso dice que fromkeys aceptará un valor, e incluso si es invocable, lo evaluará primero y luego asignará ese valor a todas las claves dict.
Las listas son mutables en Python, por lo que asignará la misma referencia de lista vacía y un cambio los afectará a todos.
Utilice defaultdict en su lugar como sigue:
Esto aceptará asignaciones a claves no existentes y los valores predeterminados serán listas vacías (en este caso).
fuente