使用pop(),list [-1]和+ =时,python中的求值顺序是什么?

fel*_*ipa 39 python list operator-precedence indices

a = [1, 2, 3]
a[-1] += a.pop()
Run Code Online (Sandbox Code Playgroud)

这导致了[1, 6].

a = [1, 2, 3]
a[0] += a.pop()
Run Code Online (Sandbox Code Playgroud)

这导致了[4, 2].什么评估顺序给出了这两个结果?

Fal*_*len 38

首先是RHS,然后是LHS.在任何一方,评估顺序都是从左到右.

a[-1] += a.pop() 是一样的, a[-1] = a[-1] + a.pop()

a = [1,2,3]
a[-1] = a[-1] + a.pop() # a = [1, 6]
Run Code Online (Sandbox Code Playgroud)

当我们改变RHS的操作顺序时,看看行为是如何变化的,

a = [1,2,3]
a[-1] = a.pop() + a[-1] # a = [1, 5]
Run Code Online (Sandbox Code Playgroud)

  • "在RHS,它是从左到右"有趣的事实:虽然运算符当然是运算符优先级的评估,但实际的表达式显然不是,例如在`f()+ g()*h()`中,函数是按f,然后g,然后h评估.例如,`a.pop()+ a.pop()*a.pop()`with`a = [3,2,1]`得到`7`(1 + 2*3) (4认同)

Chr*_*nds 22

关键的见解是,a[-1] += a.pop()语法糖a[-1] = a[-1] + a.pop().这是正确的,因为+=它被应用于不可变对象(int这里)而不是可变对象(这里是相关问题).

首先评估右侧(RHS).在RHS上:等效语法是a[-1] + a.pop().首先,a[-1]获取最后一个值3.第二,a.pop() returns 3. 3+ 36.

在左侧(LHS),a现在[1,2]由于已经应用的就地突变list.pop(),因此值a[-1]从更改26.

  • a [-1] + = a.pop()是[-1] = a [-1]的缩写+ a.pop()仅为真,因为`a`是一个int列表,我现在已经学会了.值得一提的是. (2认同)

tob*_*s_k 16

让我们来看看输出dis.disa[-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)
Run Code Online (Sandbox Code Playgroud)

这里列出了不同指令的含义.

首先,LOAD_FASTLOAD_CONST负载a-1压入堆栈,和DUP_TOP_TWO复制两个,前BINARY_SUBSCR得到下标值,从而导致a, -1, 3在栈上.然后a再次加载,并LOAD_ATTR加载pop函数,该函数不带参数调用CALL_FUNCTION.现在堆栈a, -1, 3, 3,并INPLACE_ADD添加前两个值.最后,ROT_THREE旋转堆栈以6, a, -1匹配预期的顺序,STORE_SUBSCR并存储该值.

因此,简而言之,a[-1]在调用之前评估当前值,a.pop()然后将添加的结果存储回新的a[-1],而不管其当前值.


1) 这是Python 3的反汇编,稍微压缩以更好地适应页面,添加的列显示后面的堆栈# ...; 对于Python 2,它看起来有点不同,但相似.


MSe*_*ert 6

在调试打印语句的列表周围使用薄包装可用于显示案例中的评估顺序:

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
Run Code Online (Sandbox Code Playgroud)

当我现在运行你的例子:

>>> 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
Run Code Online (Sandbox Code Playgroud)

因此,它首先获取最后一项,即3,然后弹出最后一项也是3,添加它们并覆盖列表的最后一项6.所以最终的名单将是[1, 6].

在你的第二个案件中:

>>> 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
Run Code Online (Sandbox Code Playgroud)

现在将第一个项目(1)添加到弹出的值(3)并用sum:覆盖第一个项目[4, 2].


评估的一般顺序已经由@Fallen和解释@tobias_k.这个答案只是对那里提到的一般原则的补充.