¿Cómo funciona internamente el intercambio de miembros en las tuplas de python (a, b) = (b, a)?

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 este intercambio de valores de ayb internamente? Definitivamente no está utilizando una variable temporal.

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 stack, y luego los nombres del lado izquierdo se asignan utilizando códigos de operación que toman valores de la stack nuevamente.

Para las asignaciones de tuplas con 2 o 3 elementos, Python solo usa la stack 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 

Después de los dos LOAD_FAST (que empujan un valor de una variable a la stack), la parte superior de la stack contiene [a, b] . El ROT_TWO operación ROT_TWO intercambia las dos posiciones superiores en la stack, por lo que ahora la stack tiene [b, a] en la parte superior. Los dos STORE_FAST toman esos dos valores y los almacenan en los nombres en el lado izquierdo de la asignación. El primer STORE_FAST un valor de la parte superior de la stack 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 objectives en el lado izquierdo se realicen de izquierda a derecha.

Para una asignación de 3 nombres, se ROT_THREE seguido de ROT_TWO para revertir los tres elementos principales de la stack.

Para las 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 stack con [d, c, b, a] se usa para construir una tupla (en orden inverso, BUILD_TUPLE de la stack, empujando la tupla resultante en la stack), y luego UNPACK_SEQUENCE saca la tupla de la stack de nuevo , vuelve a STORE_FAST todos los elementos de la tupla en la stack para las operaciones STORE_FAST .

El último puede parecer una operación inútil, pero el lado derecho de una asignación puede ser algo completamente diferente, una llamada de función que quizás produzca una tupla, por lo que el intérprete de Python no hace suposiciones y usa el UNPACK_SEQUENCE operación UNPACK_SEQUENCE siempre. Lo hace incluso para las operaciones de asignación de dos y tres nombres, pero un paso de optimización posterior (peephole) reemplaza una combinación BUILD_TUPLE / UNPACK_SEQUENCE con 2 o 3 argumentos con los opcodes ROT_TWO y ROT_THREE para mayor eficiencia.