Cómo transformar una cadena de claves separadas por espacios, valores pares de palabras únicas en un dict

Tengo una cadena con palabras separadas por espacios (todas las palabras son únicas, no hay duplicados). Convierto esta cadena en lista:

s = "#one cat #two dogs #three birds" out = s.split() 

Y cuenta cuántos valores se crean:

 print len(out) # Says 192 

Luego trato de borrar todo de la lista:

 for x in out: out.remove(x) 

Y luego contar de nuevo:

 print len(out) # Says 96 

¿Alguien puede explicar por favor por qué dice 96 en lugar de 0?

MÁS INFORMACIÓN

Cada línea comienza con ‘#’ y, de hecho, es un par de palabras separadas por espacios: la primera en el par es la clave y la segunda es el valor.

Entonces, lo que estoy haciendo es:

 for x in out: if '#' in x: ind = out.index(x) # Get current index nextValue = out[ind+1] # Get next value myDictionary[x] = nextValue out.remove(nextValue) out.remove(x) 

El problema es que no puedo mover todos los pares de valores clave a un diccionario ya que solo itero a través de 96 elementos.

Creo que realmente quieres algo como esto:

 s = '#one cat #two dogs #three birds' out = s.split() entries = dict([(x, y) for x, y in zip(out[::2], out[1::2])]) 

¿Qué está haciendo este código? Vamos a descomponerlo. Primero, dividimos s por espacios en blanco como lo hiciste tú.

A continuación, iteramos sobre los pares, llamándolos ” x, y “. Esos pares se convierten en una list de tuplas / pares. dict() acepta una lista de tuplas de tamaño dos y las trata como key, val .

Esto es lo que obtengo cuando lo probé:

 $ cat tryme.py s = '#one cat #two dogs #three birds' out = s.split() entries = dict([(x, y) for x, y in zip(out[::2], out[1::2])]) from pprint import pprint pprint(entries) $ python tryme.py {'#one': 'cat', '#three': 'birds', '#two': 'dogs'} 

En cuanto a lo que realmente sucedió en el bucle for :

Del Python para la documentación de la statement :

La lista de expresiones se evalúa una vez ; debe producir un objeto iterable. Se crea un iterador para el resultado de la expression_list . La serie se ejecuta una vez para cada elemento proporcionado por el iterador, en el orden de los índices ascendentes . Cada elemento, a su vez, se asigna a la lista de destino utilizando las reglas estándar para las asignaciones, y luego se ejecuta la suite. Cuando se agotan los elementos (que es inmediatamente cuando la secuencia está vacía ), la suite en la cláusula else , si está presente, se ejecuta, y el loop termina .

Creo que se muestra mejor con la ayuda de una ilustración .

Ahora, suponga que tiene un iterable object (como una list ) como este:

 out = [a, b, c, d, e, f] 

Lo que sucede cuando haces for x in out es que crea un indexador interno que funciona así (lo ilustro con el símbolo ^ ):

 [a, b, c, d, e, f] ^ <-- here is the indexer 

Lo que normalmente sucede es que: cuando finaliza un ciclo de su bucle, el indexador avanza así:

 [a, b, c, d, e, f] #cycle 1 ^ <-- here is the indexer [a, b, c, d, e, f] #cycle 2 ^ <-- here is the indexer [a, b, c, d, e, f] #cycle 3 ^ <-- here is the indexer [a, b, c, d, e, f] #cycle 4 ^ <-- here is the indexer [a, b, c, d, e, f] #cycle 5 ^ <-- here is the indexer [a, b, c, d, e, f] #cycle 6 ^ <-- here is the indexer #finish, no element is found anymore! 

Como puede ver, el indexador sigue avanzando hasta el final de su lista, ¡independientemente de lo que sucedió en la lista !

Por lo tanto, cuando se remove , esto es lo que sucedió internamente:

 [a, b, c, d, e, f] #cycle 1 ^ <-- here is the indexer [b, c, d, e, f] #cycle 1 - a is removed! ^ <-- here is the indexer [b, c, d, e, f] #cycle 2 ^ <-- here is the indexer [c, d, e, f] #cycle 2 - c is removed ^ <-- here is the indexer [c, d, e, f] #cycle 3 ^ <-- here is the indexer [c, d, f] #cycle 3 - e is removed ^ <-- here is the indexer #the for loop ends 

Observe que solo hay 3 ciclos en lugar de 6 ciclos (!!) (que es el número de elementos en la lista original). Y es por eso que te quedaste con la mitad de len de tu len original, porque esa es la cantidad de ciclos que toma completar el ciclo cuando eliminas un elemento de cada ciclo.


Si desea borrar la lista, simplemente haga lo siguiente:

 if (out != []): out.clear() 

O, alternativamente, para eliminar el elemento uno por uno, debe hacerlo al revés, desde el final hasta el principio . Uso reversed :

 for x in reversed(out): out.remove(x) 

Ahora, ¿por qué funcionaría la reversed ? Si el indexador sigue avanzando, ¿el reversed tampoco debería funcionar porque el número de elementos se reduce en uno por ciclo?

No, no es así.

¡Porque el método reversed cambia la forma en que funciona el indexador interno! Lo que sucedió cuando usas el método reversed es hacer que el indexador interno retroceda (desde el final) en lugar de avanzar .

Para ilustrar, esto es lo que normalmente sucede:

 [a, b, c, d, e, f] #cycle 1 ^ <-- here is the indexer [a, b, c, d, e, f] #cycle 2 ^ <-- here is the indexer [a, b, c, d, e, f] #cycle 3 ^ <-- here is the indexer [a, b, c, d, e, f] #cycle 4 ^ <-- here is the indexer [a, b, c, d, e, f] #cycle 5 ^ <-- here is the indexer [a, b, c, d, e, f] #cycle 6 ^ <-- here is the indexer #finish, no element is found anymore! 

Y así, cuando haces una eliminación por ciclo, no afecta a cómo funciona el indexador:

 [a, b, c, d, e, f] #cycle 1 ^ <-- here is the indexer [a, b, c, d, e] #cycle 1 - f is removed ^ <-- here is the indexer [a, b, c, d, e] #cycle 2 ^ <-- here is the indexer [a, b, c, d] #cycle 2 - e is removed ^ <-- here is the indexer [a, b, c, d] #cycle 3 ^ <-- here is the indexer [a, b, c] #cycle 3 - d is removed ^ <-- here is the indexer [a, b, c] #cycle 4 ^ <-- here is the indexer [a, b] #cycle 4 - c is removed ^ <-- here is the indexer [a, b] #cycle 5 ^ <-- here is the indexer [a] #cycle 5 - b is removed ^ <-- here is the indexer [a] #cycle 6 ^ <-- here is the indexer [] #cycle 6 - a is removed ^ <-- here is the indexer 

Espero que la ilustración te ayude a entender lo que está pasando internamente ...

No estás siendo específico. ¿Por qué estás tratando de borrar todo lo que está en la lista de salida? Si todo lo que necesita hacer es borrar la lista, ¿por qué no hacer esto?

 out = [] 

Creo que quieres seguir.

 >>> a = '#one cat #two dogs #three birds' >>> b = { x.strip().split(' ')[0] : x.strip().split(' ')[-1] for x in a.strip().split('#') if len(x) > 0 } >>> b {'three': 'birds', 'two': 'dogs', 'one': 'cat'} 

O mejor

 >>> b = [ y for x in a.strip().split('#') for y in x.strip().split(' ') if len(x) > 0 ] >>> c = { x: y for x,y in zip(b[0::2],b[1::2]) } >>> c {'three': 'birds', 'two': 'dogs', 'one': 'cat'} >>> 

El problema que está encontrando es el resultado de modificar una lista mientras se itera sobre ella. Cuando se elimina un elemento, todo lo que sigue después se mueve un índice, pero el iterador no tiene en cuenta el cambio y continúa incrementando el índice al que accedió por última vez. Por lo tanto, el iterador omite cada segundo elemento de la lista, por lo que le queda la mitad del número de elementos.

La solución directa más simple a su problema es iterar sobre una copia de out , usando notación de segmento:

 for x in out[:]: # ... out.remove(x) 

Sin embargo, hay una pregunta más profunda aquí: ¿por qué necesita eliminar los elementos de la lista? Con su algoritmo, tiene la garantía de terminar con una lista vacía, que no le sirve de nada. Sería a la vez más simple y eficiente simplemente recorrer la lista sin eliminar elementos.

Cuando haya terminado con la lista (después del bloque for-loop), puede eliminarla explícitamente (utilizando la palabra clave del ) o simplemente dejar que el sistema de recolección de basura de Python se ocupe de ella.

Sigue habiendo un problema adicional: está combinando la iteración directa sobre una lista con referencias basadas en índices. Por lo general, el uso de for x in out debe estar restringido a situaciones en las que desee acceder a cada elemento independientemente de los demás. Si desea trabajar con índices, use for i in range(len(out)) y acceda a los elementos out[i] .

Además, puede utilizar un diccionario de comprensión para realizar su tarea completa en una expresión pythonica de una línea:

 my_dictionary = {out[i]: out[i + 1] for i in range(len(out)) if "#" in out[i]} 

Otra alternativa de Pythonic sería hacer uso del hecho de que cada elemento de número par es una clave, y cada elemento de número impar es un valor (debe asumir que el resultado de la lista de str.split() sigue esto de manera consistente patrón), y usar zip en las sub-listas pares e impares.

 my_dictionary = dict(zip(out[::2], out[1::2])) 

Si solo necesitas borrar la lista,

use out = [] o out.clear()

De todos modos, lo que dijiste es porque remove función de la lista afecta a la lista.

 out = ['a', 'b', 'c', 'd', 'e', 'f'] for x in out: out.remove(x) print(x) 

entonces el resultado se muestra a continuación:

a c e

Es exactamente la mitad de la lista completa. Entonces, en tu caso, tienes 96 (la mitad de 192) de 192.

El problema es que cada vez que borra un valor de la lista, esa lista en particular restaura sus valores dinámicamente. Es decir, cuando realiza out.remove(ind) y out.remove(ind+1) , los valores en estos índices se eliminan, pero se reemplazan con valores nuevos que son antecesores del valor anterior.

Por lo tanto para evitar esto tienes que implementar el código de la siguiente manera:

 out = [] out = '#one cat #two dogs #three birds'.split() print "The list is : {0} \n".format(out) myDictionary = dict() for x in out: if '#' in x: ind = out.index(x) # Get current index nextValue = out[ind+1] # Get next value myDictionary[x] = nextValue out = [] # #emptying the list print("The dictionary is : {0} \n".format(myDictionary)) 

Por lo tanto, una vez que haya terminado de transferir los valores de la lista al diccionario, podríamos vaciarlos de forma segura utilizando out = []

El problema es que está utilizando remove (x) mientras está iterando. La variable ‘out’ se refiere tanto a la función remove como a for-loop.

Solo usa

 for i in range(len(out)): out.remove(out[i]); 

Primero se divide en ‘#’ para obtener cada registro (una cadena de clave, par de valores). Luego divide cada o en el espacio, para darle una lista de [clave, valor]. dict() permite construir el dict directamente de una lista de claves, pares de valores. Asi que:

 >>> dict( k_v.split() for k_v in s.split('#')[1:] ) {'one': 'cat', 'two': 'dogs', 'three': 'birds'} 

(Nota: tuvimos que usar s.split('#')[1:] para omitir el primer registro (en blanco)