a = [1, 2, 3]
a[-1] += a.pop()
这将导致[1, 6]
。
a = [1, 2, 3]
a[0] += a.pop()
这导致得出[4, 2]
。哪种运算顺序会得出这两个结果?
a = [1, 2, 3]
a[-1] += a.pop()
这将导致[1, 6]
。
a = [1, 2, 3]
a[0] += a.pop()
这导致得出[4, 2]
。哪种运算顺序会得出这两个结果?
先计算右手边再计算左手边。在任何一边,计算顺序都是从左往右。
a[-1] += a.pop()
与a[-1] = a[-1] + a.pop()
是等价的。
a = [1,2,3]
a[-1] = a[-1] + a.pop() # a = [1, 6]
看一下当我们改变RHS操作的顺序时,行为如何改变。
a = [1,2,3]
a[-1] = a.pop() + a[-1] # a = [1, 5]
f() + g() * h()
中,函数按照 f、g、h 的顺序进行计算。例如,对于 a = [3, 2, 1]
和 a.pop() + a.pop() * a.pop()
进行计算会得出 7
(即 1 + 2 * 3)。 - tobias_ka.pop(-1)
操作之前,a[-1]
已经被计算出来了,其值为 3
。然后进行 pop
操作,同样得到值 3
。因此,数学运算结果为 3+3
。这是因为在右侧我们从左到右逐个计算每个部分,然后再处理数学运算。 - coderforlifea[-1] += a.pop()
是 a[-1] = a[-1] + a.pop()
的语法糖。这是因为 +=
应用于不可变对象(这里是一个 int
),而不是可变对象(相关问题在这里)。
右侧表达式(RHS)先被求值。在 RHS 中:等效的语法是 a[-1] + a.pop()
。首先,a[-1]
获取最后一个值 3
。其次,a.pop()
返回 3
。 3
+ 3
是 6
。
在左侧表达式(LHS)中,由于已经应用了就地突变,因此 a
现在为 [1,2]
,因此 a[-1]
的值从 2
更改为 6
。
a
是整数列表时才成立,我现在已经学到了。值得一提。 - Simda
是字符串或元组列表,它也会像整数一样表现出相同的行为(仅对可变对象不同)。 - Chris_Rands让我们来看一下对于a[-1] += a.pop()
1)的输出结果,使用dis.dis
函数:
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)
不同指令的含义在此处列出。
首先,LOAD_FAST
和 LOAD_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.pop()
之前,计算当前的 a[-1]
的值,然后将加法的结果存储回新的 a[-1]
中,与其当前值无关。
1) 这是 Python 3 的反汇编结果,稍微压缩以更好地适应页面,添加了一列显示 # ...
后的栈;对于 Python 2,它看起来有点不同,但类似。
dis.dis
实在是很酷的一件事情,以前从没听说过这个工具! - ppasler使用一个细包装来包装一个带有调试打印语句的列表,可以用来展示你的情况下的评估顺序:
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
>>> 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
首先获取列表中的最后一项,即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
这将首先取出第一个项 (1
),将其加上弹出的值(3
),并用总和覆盖了第一个项: [4, 2]
。
针对您的具体示例
a[-1] += a.pop() #is the same as
a[-1] = a[-1] + a.pop() # a[-1] = 3 + 3
排序:
=
操作后评估 a[-1]
pop()
,减少 a
的长度问题在于,在执行 pop()
操作后,a[-1]
变成了 a[1]
的值(之前是 a[2]
),但这发生在赋值操作之前。
a[0] = a[0] + a.pop()
正常工作
=
后评估a[0]
pop()
此示例说明,为什么在操作列表时(通常是for循环)不应该对其进行修改。在这种情况下始终使用副本。
a[-1] += a.pop()
翻译为a[-1] = a[-1] + a.pop()
。这就是为什么你得到了6
。在执行a.pop()
之前,先计算a[-1]
。如果你将其改为a[-1] = a.pop() + a[-1]
,则会得到5
。 - Ma0a += b
不等同于a = a + b
,且 Python 不能保证将其翻译为相同结果。不同的方法会被调用,是否最终得到相同结果取决于操作数的类型。 - Alex Riley+
和+=
操作符在整数中等效这一事实是回答这个问题的关键:如果涉及不同对象,一般结果将是相同的。我认为评估顺序是主要要点。) - Alex Rileya = [1, 2, 3]; temp = a.pop(); a[-1] = 2 * temp
或者a = [1, 2, 3]; temp = a.pop(); a[-1] += temp
,具体取决于你想要做什么。这样可以明确你预期的评估顺序,并且更容易正确实现。 - jpmc26