python list loops split iteration

python - Cómo transformar una cadena de clave separada por espacios, pares de valores de palabras únicas en un dict



list loops (9)

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''} >>>

Tengo una cadena con palabras que están separadas por espacios (todas las palabras son únicas, sin duplicados). Convierto esta cadena en lista:

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

Y cuente cuántos valores se crean:

print len(out) # Says 192

Luego trato de eliminar todo de la lista:

for x in out: out.remove(x)

Y luego cuenta de nuevo:

print len(out) # Says 96

¿Alguien puede explicar 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 la pareja 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 recorro 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 desglosarlo. Primero, dividimos s espacios en blanco como lo hiciste.

Luego iteramos sobre los pares out adentro, 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''}


El problema con el que se encuentra es el resultado de modificar una lista mientras itera sobre ella. Cuando se elimina un elemento, todo después de que un índice lo mueve hacia adelante, 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 , utilizando la notación de corte:

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

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

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

Sigue habiendo un problema adicional: está combinando la iteración directa sobre una lista con referencias basadas en índices. El uso de for x in out normalmente debe restringirse 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 elementos sin out[i] .

Además, puede usar la comprensión de un diccionario para realizar toda su tarea en una expresión pitónica de una línea:

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

Otra alternativa pitónica sería hacer uso del hecho de que cada elemento par es una clave, y cada elemento impar es un valor (tendrías que asumir que el resultado de la lista de str.split() sigue consistentemente esto patrón) y use zip en las sublistas pares e impares.

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


El problema es que cada vez que elimina un valor de la lista, esa lista 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 nuevos valores que son predecesores del valor anterior.

Por lo tanto, para evitar esto, debe 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))

Entonces, una vez que haya terminado de transferir los valores de la lista al diccionario, podríamos vaciar la out forma segura usando out = []


El problema es que estás usando remove (x) mientras iteras. 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]);


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

Desde Python para la documentación de la declaración :

La lista de expresiones se evalúa una vez ; debería producir un objeto iterable. Se crea un iterador para el resultado de la expression_list . La suite se ejecuta una vez para cada elemento proporcionado por el iterador, en el orden de los índices ascendentes . A su vez, cada elemento se asigna a la lista de objetivos utilizando las reglas estándar para las asignaciones, y luego se ejecuta el conjunto. Cuando se agotan los elementos (que es inmediatamente cuando la secuencia está vacía ), el conjunto 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 la 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 es así (lo ilustra con el símbolo ^ ):

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

Lo que normalmente sucede es que: cuando finalizas un ciclo de tu ciclo, 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 le haya sucedido !

Por lo tanto, cuando 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

Tenga en cuenta que solo hay 3 ciclos allí 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 la len original, porque esa es la cantidad de ciclos que se necesitan para completar el ciclo cuando eliminas un elemento de cada ciclo.

Si desea borrar la lista, simplemente haga:

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 lo reversed ? Si el indexador sigue avanzando, ¿no se reversed tampoco debería funcionar porque el número de elementos se reduce en uno por ciclo de todos modos?

No, no es así

¡Porque el método reversed cambia la forma en que funciona el indexador interno! Lo que sucede cuando usa el método reversed es hacer que el indexador interno se mueva hacia atrás (desde el final) en lugar de hacia adelante .

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, por lo tanto, cuando realiza una eliminación por ciclo, no afecta el funcionamiento del 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 comprender lo que está sucediendo internamente ...


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

out = []


Primero se divide en ''#'' para obtener cada registro (una cadena de clave, par de valores). Luego divide cada o en el espacio, para obtener una lista de [clave, valor]. dict() permite construir el dict directamente desde una lista de pares clave-valor. 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))


Si solo necesita borrar la lista,

use out = [] o out.clear()

De todos modos, lo que dijiste es porque la función remove 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 su caso, obtuvo 96 (la mitad de 192) de 192.