Problema de asignación múltiple de Python (lista)

>>> i = 1 >>> A = [3,4,-1,1] >>> A[A[i] - 1], A[i] = A[i], A[A[i] - 1] >>> A [3, 1, -1, 4] >>> A = [3,4,-1,1] >>> A[i], A[A[i] - 1] = A[A[i] - 1], A[i] >>> A [4, 1, -1, 1] 

Tengo una pregunta cuando hago la asignación de múltiples variables para una lista. Como el ejemplo anterior, la tarea

 A[A[i] - 1], A[i] = A[i], A[A[i] - 1] 

es diferente de la tarea

 A[i], A[A[i] - 1] = A[A[i] - 1], A[i] 

Estoy realmente confundido con el orden de cálculo interno en Python. ¿Por qué los resultados son diferentes? ¿Cuál es la mejor manera de hacer este tipo de asignación múltiple en una línea?

Según la documentación :

Python evalúa expresiones de izquierda a derecha. Observe que al evaluar una asignación, el lado derecho se evalúa antes que el lado izquierdo.

Para más detalles, vea esta sección . Aquí hay un buen ejemplo de un rompecabezas que explota este comportamiento.

Esto significa que el lado derecho de = se evalúa primero, de izquierda a derecha, luego la asignación se realiza en el lado izquierdo, de izquierda a derecha. Necesariamente, los paréntesis son evaluados de adentro hacia afuera. Desglosando paso a paso, aquí está el primer ejemplo:

 i = 1 A = [3, 4, -1, 1] A[A[i] - 1], A[i] = A[i], A[A[i] - 1] = A[1], A[A[i] - 1] = 4, A[A[i] - 1] = 4, A[A[1] - 1] = 4, A[4 - 1] = 4, A[3] = 4, 1 A[A[i] - 1], A[i] = 4, 1 A[A[1] - 1], A[i] = 4, 1 A[4 - 1], A[i] = 4, 1 A[3], A[i] = 4, 1 # A becomes [3, 4, -1, 4] A[i] = 1 A[1] = 1 # A becomes [3, 1, -1, 4] 

Y aquí está el segundo:

 i = 1 A = [3, 4, -1, 1] A[i], A[A[i] - 1] = A[A[i] - 1], A[i] = A[A[1] - 1], A[i] = A[4 - 1], A[i] = A[3], A[i] = 1, A[i] = 1, A[1] = 1, 4 A[i], A[A[i] - 1] = 1, 4 A[1], A[A[i] - 1] = 1, 4 # A becomes [3, 1, -1, 1] A[A[1] - 1] = 4 A[1 - 1] = 4 A[0] = 4 # A becomes [4, 1, -1, 1] 

La asignación al objective de la izquierda en el lado izquierdo altera el contenido de A , lo que cambia la indexación en el objective de la derecha. El 4 se asigna a A[3] o A[0] , dependiendo del valor de A[1] (que cambia de 4 a 1 ) cuando se calcula el índice.

“¿Cuál es la mejor manera de hacer este tipo de asignación múltiple en una línea?” – Haría mi mejor esfuerzo para evitarlo. No puedo pensar en ninguna situación en la que sea necesario asignarlo a objectives en movimiento como este.

Con la forma en que se lo alienta a hacer cosas como a, b = b, a+b , puede pensar que se supone que las múltiples asignaciones como esta siempre evitan los problemas con una asignación que interfiere con otra. En particular, es natural pensar que todas las evaluaciones de expresiones ocurren antes de cualquier tarea. Desafortunadamente, no es así como funciona.

Cuando tienes una asignación múltiple del formulario

 expr_a[expr_b], expr_c[expr_d] = expr_e, expr_f 

El orden de los eventos es el siguiente:

  1. Evalúe el lado derecho, evaluando expr_e y expr_f .
  2. Realice la primera asignación, evaluando expr_a y expr_b .
  3. Realice la tercera asignación, evaluando expr_c y expr_d . La primera asignación ya sucedió cuando se evalúan estas expresiones.

Eso significa que si la asignación a expr_a[expr_b] cambia el valor de expr_c o expr_d , eso cambiará lo que sucede en la segunda asignación.

En su caso, la asignación a A[i] cambia el valor de A[i] - 1 en el objective de asignación A[A[i] - 1] .


No utilice una asignación múltiple en casos como este. Separe las asignaciones en sus propias líneas y use variables temporales si es necesario para recordar los valores que cambian las asignaciones.

La secuencia de operaciones funciona igual para cualquier asignación múltiple:

  1. Evalúa cada expresión en el RHS (lado derecho), de izquierda a derecha, colocando cada una en una variable temporal.
  2. Evalúa las expresiones en el LHS (izquierda), de izquierda a derecha. Para cada uno de estos, asigne la variable temporal correspondiente del RHS.

Para su código, esto se expande a:

 # A[A[i] - 1], A[i] = A[i], A[A[i] - 1] A = [3,4,-1,1] t1 = A[i] t2 = A[A[i] - 1] # t1 = 4, t2 = 1 A[A[i] - 1] = t1 A[i] = t2 print A # Result: [3, 1, -1, 4] # A[i], A[A[i] - 1] = A[A[i] - 1], A[i] A = [3,4,-1,1] t2 = A[A[i] - 1] t1 = A[i] # As before, t1 = 4, t2 = 1 A[i] = t2 # A[i] is now 1 ! A[A[i] - 1] = t1 print A # Result: [4, 1, -1, 1] 

La diferencia crítica es cuando A [i] se cambia en cada secuencia. En el segundo ejemplo, cuando evaluamos A [A [i] – 1] en la asignación final, A [i] ya ha cambiado a 1. Por lo tanto, esta última asignación pone 4 en la ubicación 0, en lugar de la ubicación 3.