CPython is implemented as a stack machine. If you want to see exactly what order the subexpressions are evaluated, it can be helpful to disassemble them:
>>> from dis import dis
>>> dis('''
... available_items = {"health potion": 10, "cake of the cure": 5, "green elixir": 20, "strength sandwich": 25, "stamina grains": 15, "power stew": 30}
...
... health_points = 20
...
... health_points += available_items.pop("stamina grains")
... ''')
2 0 LOAD_CONST 0 (10)
2 LOAD_CONST 1 (5)
4 LOAD_CONST 2 (20)
6 LOAD_CONST 3 (25)
8 LOAD_CONST 4 (15)
10 LOAD_CONST 5 (30)
12 LOAD_CONST 6 (('health potion', 'cake of the cure', 'green elixir', 'strength sandwich', 'stamina grains', 'power stew'))
14 BUILD_CONST_KEY_MAP 6
16 STORE_NAME 0 (available_items)
4 18 LOAD_CONST 2 (20)
20 STORE_NAME 1 (health_points)
6 22 LOAD_NAME 1 (health_points)
24 LOAD_NAME 0 (available_items)
26 LOAD_METHOD 2 (pop)
28 LOAD_CONST 7 ('stamina grains')
30 CALL_METHOD 1
32 INPLACE_ADD
34 STORE_NAME 1 (health_points)
36 LOAD_CONST 8 (None)
38 RETURN_VALUE
While subexpressions (mostly) evaluate left-to-right, operator precendence and parentheses can change this. And the assignment is a statement, not an expression. Even though the +=
is to the left of the .pop()
, you can see that the call happens before the assignment in the compiled bytecode.
Notice that the (pop)
call pushes its return value on the stack at the CALL_METHOD
instruction, so it can be used by the INPLACE_ADD
. At that time, the value is not referenced in the dict, only on the stack, but at no point has the value been lost. And the result of the addition is available to the STORE_NAME
instruction after that.