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.

¿Fue útil?

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.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top