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áusulaelse
, si está presente, se ejecuta y elloop
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étodoreversed
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.