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_groupsdebe 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

getdevuelveNonesi 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).binariesno 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
forbucle 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é
getfunciona? 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+=bes justoa=a+b, pero la implementación puede ser diferente para la optimización; en el caso de las listas,+=es solo.extendporque 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] * 5y cómo lomylist = [[] for x in range(5)] * 5habrí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
fromkeysmé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