¿Cómo funciona internamente el intercambio de miembros en las tuplas de Python (a,b)=(b,a)?
-
21-12-2019 - |
Pregunta
In [55]: a = 5
In [56]: b = 6
In [57]: (a, b) = (b, a)
In [58]: a
Out[58]: 6
In [59]: b
Out[59]: 5
¿Cómo funciona internamente este intercambio de valores de a y b?Definitivamente no está usando una variable temporal.
Solución
Python separa la expresión del lado derecho de la asignación del lado izquierdo.Primero se evalúa el lado derecho y el resultado se almacena en la pila, y luego los nombres del lado izquierdo se asignan usando códigos de operación que toman valores. de la pila nuevamente.
Para asignaciones de tuplas con 2 o 3 elementos, Python simplemente usa la pila directamente:
>>> import dis
>>> def foo(a, b):
... a, b = b, a
...
>>> dis.dis(foo)
2 0 LOAD_FAST 1 (b)
3 LOAD_FAST 0 (a)
6 ROT_TWO
7 STORE_FAST 0 (a)
10 STORE_FAST 1 (b)
13 LOAD_CONST 0 (None)
16 RETURN_VALUE
despues de los dos LOAD_FAST
códigos de operación (que empujan un valor de una variable a la pila), la parte superior de la pila contiene [a, b]
.El ROT_TWO
código de operación intercambia las dos primeras posiciones de la pila para que la pila ahora tenga [b, a]
en la cima.Los dos STORE_FAST
códigos de operación luego toma esos dos valores y los almacena en los nombres en el lado izquierdo de la tarea.La primera STORE_FAST
saca un valor de la parte superior de la pila y lo coloca en a
, el siguiente aparece nuevamente, almacenando el valor en b
.La rotación es necesaria porque Python garantiza que las asignaciones en una lista de objetivos en el lado izquierdo se realicen de izquierda a derecha.
Para una asignación de 3 nombres, ROT_THREE
seguido por ROT_TWO
se ejecuta para invertir los tres elementos superiores de la pila.
Para asignaciones más largas del lado izquierdo, se construye una tupla explícita:
>>> def bar(a, b, c, d):
... d, c, b, a = a, b, c, d
...
>>> dis.dis(bar)
2 0 LOAD_FAST 0 (a)
3 LOAD_FAST 1 (b)
6 LOAD_FAST 2 (c)
9 LOAD_FAST 3 (d)
12 BUILD_TUPLE 4
15 UNPACK_SEQUENCE 4
18 STORE_FAST 3 (d)
21 STORE_FAST 2 (c)
24 STORE_FAST 1 (b)
27 STORE_FAST 0 (a)
30 LOAD_CONST 0 (None)
33 RETURN_VALUE
Aquí la pila con [d, c, b, a]
se utiliza para construir una tupla (en orden inverso, BUILD_TUPLE
sale de la pila nuevamente, empujando la tupla resultante hacia la pila), y luego UNPACK_SEQUENCE
saca la tupla de la pila nuevamente, empuja todos los elementos de la tupla nuevamente a la pila nuevamente para el STORE_FAST
operaciones.
Esto último puede parecer una operación inútil, pero el lado derecho de una tarea puede ser algo completamente diferente, una llamada a función que produce tal vez una tupla, por lo que el intérprete de Python no hace suposiciones y utiliza la UNPACK_SEQUENCE
código de operación siempre.Lo hace incluso para las operaciones de asignación de dos y tres nombres, pero un paso de optimización posterior (mirilla) reemplaza un BUILD_TUPLE
/ UNPACK_SEQUENCE
combinación con 2 o 3 argumentos con lo anterior ROT_TWO
y ROT_THREE
Códigos de operación para mayor eficiencia.