Como todos sabemos, la forma pitónica de intercambiar los valores de dos elementos a
y b
es
a, b = b, a
y debe ser equivalente a
b, a = a, b
Sin embargo, hoy, cuando estaba trabajando en un código, descubrí accidentalmente que los siguientes dos intercambios dan resultados diferentes:
nums = [1, 2, 4, 3] i = 2 nums[i], nums[nums[i]-1] = nums[nums[i]-1], nums[i] print(nums) # [1, 2, 4, 3] nums = [1, 2, 4, 3] i = 2 nums[nums[i]-1], nums[i] = nums[i], nums[nums[i]-1] print(nums) # [1, 2, 3, 4]
Esto es alucinante para mí. ¿Alguien puede explicarme qué pasó aquí? Pensé que en un intercambio de Python las dos asignaciones ocurren de forma simultánea e independiente.
De python.org
La asignación de un objeto a una lista de destino, opcionalmente entre paréntesis o corchetes, se define recursivamente de la siguiente manera.
...
- De lo contrario: el objeto debe ser iterable con la misma cantidad de elementos que objetivos en la lista de objetivos, y los elementos se asignan, de izquierda a derecha, a los objetivos correspondientes.
Así que interpreto que eso significa que su tarea
nums[i], nums[nums[i]-1] = nums[nums[i]-1], nums[i]
es aproximadamente equivalente a
tmp = nums[nums[i]-1], nums[i] nums[i] = tmp[0] nums[nums[i] - 1] = tmp[1]
(con una mejor verificación de errores, por supuesto)
mientras que el otro
nums[nums[i]-1], nums[i] = nums[i], nums[nums[i]-1]
es como
tmp = nums[i], nums[nums[i]-1] nums[nums[i] - 1] = tmp[0] nums[i] = tmp[1]
Entonces, el lado derecho se evalúa primero en ambos casos. Pero luego las dos piezas del lado izquierdo se evalúan en orden y las asignaciones se realizan inmediatamente después de la evaluación. Crucialmente, esto significa que el segundo término en el lado izquierdo solo se evalúa después de que ya se haya realizado la primera asignación. Entonces, si actualiza nums[i]
primero, entonces nums[nums[i] - 1]
se refiere a un índice diferente que si actualiza nums[i]
segundo lugar.
Esto se debe a que la evaluación, específicamente en el lado izquierdo de =
, ocurre de izquierda a derecha:
nums[i], nums[nums[i]-1] =
Primero se asigna nums[i]
, y luego ese valor se usa para determinar el índice en la asignación a nums[nums[i]-1]
Al hacer la tarea así:
nums[nums[i]-1], nums[i] =
... el índice de nums[nums[i]-1]
depende del valor antiguo de nums[i]
, ya que la asignación a nums[i]
sigue después...
Esto sucede de acuerdo con las reglas:
Entonces, con nums = [1, 2, 4, 3]
, tu código en el primer caso
nums[2], nums[nums[2]-1] = nums[nums[2]-1], nums[2]
es equivalente a:
nums[2], nums[nums[2]-1] = nums[nums[2]-1], nums[2] nums[2], nums[nums[2]-1] = nums[3], nums[2] nums[2], nums[nums[2]-1] = 3, 4
y como ahora se evalúa el lado derecho, las asignaciones son equivalentes a:
nums[2] = 3 nums[nums[2]-1] = 4 nums[2] = 3 nums[3-1] = 4 nums[2] = 3 nums[2] = 4
lo que da:
print(nums) # [1, 2, 4, 3]
En el segundo caso, obtenemos:
nums[nums[2]-1], nums[2] = nums[2], nums[nums[2]-1] nums[nums[2]-1], nums[2] = nums[2], nums[3] nums[nums[2]-1], nums[2] = 4, 3 nums[nums[2]-1] = 4 nums[2] = 3 nums[4-1] = 4 nums[2] = 3 nums[3] = 4 nums[2] = 3 print(nums) # [1, 2, 3, 4]