a = [1, 2, 3]
a[-1] += a.pop()
This results in [1, 6]
.
a = [1, 2, 3]
a[0] += a.pop()
This results in [4, 2]
. What order of evaluation gives these two results?
a = [1, 2, 3]
a[-1] += a.pop()
This results in [1, 6]
.
a = [1, 2, 3]
a[0] += a.pop()
This results in [4, 2]
. What order of evaluation gives these two results?
Let's have a look at the output of
dis.dis
fora[-1] += a.pop()
1):The meaning of the different instructions is listed here.
First,
LOAD_FAST
andLOAD_CONST
loada
and-1
onto the stack, andDUP_TOP_TWO
duplicates the two, beforeBINARY_SUBSCR
gets the subscript value, resulting ina, -1, 3
on the stack. It then loadsa
again, andLOAD_ATTR
loads thepop
function, which is called with no arguments byCALL_FUNCTION
. The stack is nowa, -1, 3, 3
, andINPLACE_ADD
adds the top two values. Finally,ROT_THREE
rotates the stack to6, a, -1
to match the order expected bySTORE_SUBSCR
and the value is stored.So, in short, the current value of
a[-1]
is evaluated before callinga.pop()
and the result of the addition is then stored back to the newa[-1]
, irrespective of its current value.1) This is the disassembly for Python 3, slightly compressed to better fit on the page, with an added column showing the stack after
# ...
; for Python 2 it looks a bit different, but similar.For you specific example
Order:
a[-1]
after=
pop()
, decreasing the length ofa
The thing is, that
a[-1]
becomes the value ofa[1]
(wasa[2]
) after thepop()
, but this happens before the assignment.Works as expected
a[0]
after=
pop()
This example shows, why you shouldn't manipulate a list while working on it (commonly said for loops). Always work on copys in this case.
Using a thin wrapper around a list with debugging print-statements can be used to show the order of evaluation in your cases:
When I now run your example:
So it starts by getting the last item, which is 3, then pops the last item which is also 3, adds them and overwrites the last item of your list with
6
. So the final list will be[1, 6]
.And in your second case:
This now takes the first item (
1
) adds it to the popped value (3
) and overwrites the first item with the sum:[4, 2]
.The general order of evaluation is already explained by
@Fallen
and@tobias_k
. This answer just supplements the general principle mentioned there.RHS first and then LHS. And at any side, the evaluation order is left to right.
a[-1] += a.pop()
is same as,a[-1] = a[-1] + a.pop()
See how the behavior changes when we change the order of the operations at RHS,
The key insight is that
a[-1] += a.pop()
is syntactic sugar fora[-1] = a[-1] + a.pop()
. This holds true because+=
is being applied to an immutable object (anint
here) rather than a mutable object (relevant question here).The right hand side (RHS) is evaluated first. On the RHS: equivalent syntax is
a[-1] + a.pop()
. First,a[-1]
gets the last value3
. Second,a.pop()
return
s3
.3
+3
is6
.On the Left hand side (LHS),
a
is now[1,2]
due to the in-place mutation already applied bylist.pop()
and so the value ofa[-1]
is changed from2
to6
.