在 python 中评估?

Evaluation in python?

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")

当我 运行 此代码时,添加了键值 "stamina grains" 并从字典 available_items 中删除了耐力颗粒 available_items

但是 Python 从左到右评估表达式,因此它应该首先删除键 "stamina grains" 因此不应向 health_points.

添加任何内容

我对 Python 如何计算表达式感到困惑。有人可以澄清一下并给我一些关于 Python 如何计算表达式的资源吗?

CPython 作为 stack machine 实现。如果您想准确查看子表达式的计算顺序,反汇编它们会很有帮助:

>>> 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

虽然子表达式(大部分)从左到右求值,但运算符优先级和括号可以改变这一点。赋值是一个语句,而不是表达式。即使 +=.pop() 的左侧,您也可以看到调用发生在编译后的字节码中的赋值之前。

请注意,(pop) 调用将其 return 值压入堆栈中的 CALL_METHOD 指令,因此它可以被 INPLACE_ADD 使用。那时,该值未在字典中引用,仅在堆栈中引用,但该值从未丢失。加法的结果可用于之后的 STORE_NAME 指令。

来自first Google result搜索"Python+dictionary +pop"(重点是我的):

The pop() method removes and returns an element from a dictionary having the given key.

所以,确实pop动作是第一个执行的,从字典中删除元素也是正确的,但它也returns 该值,用于更新 healt_points 变量。

详细,根据evaluation order table

Python evaluates expressions from left to right. Notice that while evaluating an assignment, the right-hand side is evaluated before the left-hand side.

所以,考虑表达式

health_points = health_points  + available_items.pop("stamina grains")
  1. 我们有一个作业,所以首先计算右边:health_points + available_items.pop("stamina grains")
  2. 右边,从左到右计算:health_points先计算,它的值为20
  3. 现在评估 available_items.pop("stamina grains")。为 available_items 字典
  4. 调用 pop 方法
  5. 找到键"stamina grains",返回其值(15)
  6. 作为副作用pop 从字典中删除了 "stamina grains"。无论如何都会返回值 15:它只是一个表示方法结果的整数。不再受限于字典元素的存在
  7. 表达式现在是 health_points = 20 + 15。右侧评估是整数 35,现在可以将其分配给 health_points。这完成了左侧评估

如果你看这里:Operator precedence,你可以看到 "call" 靠近 table 的底部,所以它首先被执行。执行完第一步后,您可以将语句重写为: health_points += 15.

再高一点就是加法,接下来执行(+=是加法的简写:health_points = health_points + available_items.pop("stamina grains"))。最上面是赋值,所以这是最后执行的。导致 health_points == 35.