>>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] = 4Entonces, 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 4Ok, 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!