tuple index dict comprehension python list operator-precedence pop indices

index - ¿Cuál es el orden de evaluación en python cuando se usa pop(), list[-1] y+=?



tuple python (5)

Echemos un vistazo a la salida de dis.dis para a[-1] += a.pop() 1) :

3 15 LOAD_FAST 0 (a) # a, 18 LOAD_CONST 5 (-1) # a, -1 21 DUP_TOP_TWO # a, -1, a, -1 22 BINARY_SUBSCR # a, -1, 3 23 LOAD_FAST 0 (a) # a, -1, 3, a 26 LOAD_ATTR 0 (pop) # a, -1, 3, a.pop 29 CALL_FUNCTION 0 (0 positional, 0 keyword pair) # a, -1, 3, 3 32 INPLACE_ADD # a, -1, 6 33 ROT_THREE # 6, a, -1 34 STORE_SUBSCR # (empty)

El significado de las diferentes instrucciones se enumera here .

Primero, LOAD_FAST y LOAD_CONST cargan a y -1 en la pila, y DUP_TOP_TWO duplica los dos, antes de que BINARY_SUBSCR obtenga el valor del subíndice, lo que resulta en a, -1, 3 en la pila. A continuación, vuelve a cargar a , y LOAD_ATTR carga la función pop , a la que CALL_FUNCTION llama sin argumentos. La pila ahora es a, -1, 3, 3 , e INPLACE_ADD agrega los dos valores superiores. Finalmente, ROT_THREE rota la pila a 6, a, -1 para que coincida con el orden esperado por STORE_SUBSCR y el valor se almacena.

Entonces, en resumen, el valor actual de a[-1] se evalúa antes de llamar a a.pop() y el resultado de la adición se almacena nuevamente en el nuevo a[-1] , independientemente de su valor actual.

1) Este es el desmontaje de Python 3, ligeramente comprimido para ajustarse mejor a la página, con una columna agregada que muestra la pila después de # ... ; para Python 2 se ve un poco diferente, pero similar.

a = [1, 2, 3] a[-1] += a.pop()

Esto resulta en [1, 6] .

a = [1, 2, 3] a[0] += a.pop()

Esto resulta en [4, 2] . ¿Qué orden de evaluación da estos dos resultados?


El uso de una envoltura delgada alrededor de una lista con declaraciones de impresión de depuración se puede usar para mostrar el orden de evaluación en sus casos:

class Test(object): def __init__(self, lst): self.lst = lst def __getitem__(self, item): print(''in getitem'', self.lst, item) return self.lst[item] def __setitem__(self, item, value): print(''in setitem'', self.lst, item, value) self.lst[item] = value def pop(self): item = self.lst.pop() print(''in pop, returning'', item) return item

Cuando ahora corro tu ejemplo:

>>> a = Test([1, 2, 3]) >>> a[-1] += a.pop() in getitem [1, 2, 3] -1 in pop, returning 3 in setitem [1, 2] -1 6

Entonces comienza por obtener el último elemento, que es 3, luego aparece el último elemento que también es 3, los agrega y sobrescribe el último elemento de su lista con 6 . Así que la lista final será [1, 6] .

Y en tu segundo caso:

>>> a = Test([1, 2, 3]) >>> a[0] += a.pop() in getitem [1, 2, 3] 0 in pop, returning 3 in setitem [1, 2] 0 4

Esto toma ahora el primer elemento ( 1 ), lo agrega al valor emergente ( 3 ) y sobrescribe el primer elemento con la suma: [4, 2] .

El orden general de evaluación ya está explicado por @Fallen y @tobias_k . Esta respuesta solo complementa el principio general mencionado allí.


La idea clave es que a[-1] += a.pop() es azúcar sintáctico para a[-1] = a[-1] + a.pop() . Esto es cierto porque += se aplica a un objeto inmutable (un int aquí) en lugar de a un objeto mutable ( pregunta relevante aquí ).

El lado derecho (RHS) se evalúa primero. En la RHS: la sintaxis equivalente es a[-1] + a.pop() . Primero, a[-1] obtiene el último valor 3 . Segundo, a.pop() return s 3 . 3 + 3 es 6 .

En el lado izquierdo (LHS), a ahora es [1,2] debido a la mutación in situ ya aplicada por list.pop() por lo que el valor de a[-1] se cambia de 2 a 6 .


Para tu ejemplo especifico

a[-1] += a.pop() #is the same as a[-1] = a[-1] + a.pop() # a[-1] = 3 + 3

Orden:

  1. evaluar a[-1] después de =
  2. pop() , disminuyendo la longitud de a
  3. adición
  4. asignación

La cosa es que a[-1] convierte en el valor de a[1] (era a[2] ) después del pop() , pero esto sucede antes de la asignación.

a[0] = a[0] + a.pop()

Funciona como se espera

  1. evaluar a[0] después de =
  2. pop()
  3. adición
  4. asignación

Este ejemplo muestra, por qué no debe manipular una lista mientras trabaja en ella (comúnmente se dice para los bucles). Trabajar siempre en copys en este caso.


RHS primero y luego LHS. Y en cualquier lado, el orden de evaluación es de izquierda a derecha.

a[-1] += a.pop() es igual que, a[-1] = a[-1] + a.pop()

a = [1,2,3] a[-1] = a[-1] + a.pop() # a = [1, 6]

Vea cómo cambia el comportamiento cuando cambiamos el orden de las operaciones en RHS,

a = [1,2,3] a[-1] = a.pop() + a[-1] # a = [1, 5]