>>arr = [4, 2, 1, 3] >>arr[0], arr[arr[0]-1] = arr[arr[0]-1], arr[0] >>arr
Resultado que espero >>[3, 2, 1, 4]
Resultado que obtengo >>[3, 2, 4, 3]
Básicamente, estoy tratando de intercambiar el n. ° 4 y el n. ° 3 (en mi problema real, el índice no será 0, sino un iterador "i". Así que no puedo simplemente hacer arr[0], arr[3] = arr[ 3], arr[0]) Pensé que entendía bastante bien la asignación simultánea. Aparentemente estaba equivocado. No entiendo por qué arr[arr[0]-1] en el lado izquierdo de la tarea se evalúa como arr[2] en lugar de arr[3]. Si las asignaciones ocurren simultáneamente (evaluado desde la derecha),
arr[0] (dentro del índice del segundo elemento a la izquierda) aún debe ser "4"
arr[0] -1 (el índice del segundo elemento a la izquierda) debería ser "3"
Porque la lista de objetivos no se evalúa simultáneamente . Aquí está la sección relevante de los documentos:
El objeto debe ser iterable con el mismo número de elementos que objetivos en la lista de objetivos, y los elementos se asignan, de izquierda a derecha, a los objetivos correspondientes.
Dos cosas a tener en cuenta, el lado derecho evalúa primero la expresión. Entonces, en el RHS, primero creamos la tupla:
(3, 4)
Tenga en cuenta que eso se hace de izquierda a derecha . Ahora, la asignación a cada objetivo en la lista de objetivos de la izquierda se realiza en orden:
arr[0] = 3
Luego, el siguiente objetivo, arr[0]
es 3
y 3-1 es 2
arr[2] = 4
Entonces, una solución simple es simplemente calcular los índices primero antes del intercambio:
>>> arr = [4, 2, 1, 3] >>> i, j = arr[0] - 1, 0 >>> arr[j], arr[i] = arr[i], arr[j] >>> arr [3, 2, 1, 4]
Aquí hay una demostración usando una lista detallada que podemos definir fácilmente:
>>> class NoisyList(list): ... def __getitem__(self, item): ... value = super().__getitem__(item) ... print("GETTING", item, "value of", value) ... return value ... def __setitem__(self, item, value): ... print("SETTING", item, 'with', value) ... super().__setitem__(item, value) ... >>> arr = NoisyList([4, 2, 1, 3]) >>> arr[0], arr[arr[0]-1] = arr[arr[0]-1], arr[0] GETTING 0 value of 4 GETTING 3 value of 3 GETTING 0 value of 4 SETTING 0 with 3 GETTING 0 value of 3 SETTING 2 with 4
Ok, esto sucede porque el arr[0]
se cambia a 3
cuando le asignas arr[arr[0]-1]
. Y después de eso, cuando Python echa un vistazo a arr[arr[0]-1]
e intenta asignarle el valor de arr[0]
si encuentra que arr[0]
es 3
porque en la asignación anterior lo ha cambiado. Una pequeña demostración:
arr = [4, 2, 1, 3] arr[0], arr[arr[0]-1] = arr[arr[0]-1], arr[0] │ │ └──────────┬────────────────┘ │ │ these are done first and the list becomes: [3, 2, 1, 3] Next when the python takes a look at these two, it: │ ┌────────────┴───────────────┐ │ │ arr[0], arr[arr[0]-1] = arr[arr[0]-1], arr[0] it finds the `arr[0]` to be `3` so, it assigns `3` to the `3rd` element because `arr[arr[0]-1]` is 3.
La sustitución de los dos valores no es verdaderamente simultánea; se manejan en orden de izquierda a derecha. Entonces, alterar arr
durante ese proceso conduce a este comportamiento.
Considere este ejemplo alternativo:
>>> arr = [1, 2, 3] >>> arr[0], arr[arr[0]] = 10, 5 ...
Con una reasignación simultánea hipotética, tratamos de reemplazar el primer valor de arr
con 10
y luego el elemento arr[0]
1
( también conocido como 1st ) con 5
. Entonces, con suerte , obtenemos [10, 5, 3]
. Pero esto falla con IndexError: list assignment index out of range
. Si luego inspecciona arr
después de este error:
>>> arr [10, 2, 3]
La primera tarea se completó, pero la segunda fracasó. Cuando se trata de la segunda asignación (después de la coma), no se puede encontrar el valor real arr[0]
th (también conocido como 10
) (b/c la lista no es tan larga).
Este comportamiento también se puede ver especificando claramente que la segunda asignación falle (todavía especificando un índice fuera de rango):
>>> arr = [1, 2, 3] >>> arr[0], arr[99] = 5, 6 # Same index error, but arr becomes [5, 2, 3]
Esto recuerda a modificar una lista sobre la que está iterando , lo que a veces es factible pero a menudo se desaconseja porque genera problemas como lo que está viendo.
Una alternativa es crear una copia (esta es a veces una solución para modificar la lista sobre la que está iterando) y usarla para hacer referencia a los valores en arr
:
>>> arr = [4, 2, 1, 3] >>> copy = arr.copy() >>> arr[0], arr[copy[0]-1] = copy[copy[0]-1], copy[0] >>> arr [3, 2, 1, 4]
Aunque es bastante feo aquí. La alternativa en la respuesta aceptada es mucho mejor, ¡o este enfoque idiomático probablemente también debería funcionar!