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)
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()
return
s 3
.
3
+ 3
是6
.
在左侧(LHS),a
现在[1,2]
由于已经应用的就地突变list.pop()
,因此值a[-1]
从更改2
为6
.
tob*_*s_k 16
让我们来看看输出dis.dis
为a[-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_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[-1]
在调用之前评估当前值,a.pop()
然后将添加的结果存储回新的a[-1]
,而不管其当前值.
1) 这是Python 3的反汇编,稍微压缩以更好地适应页面,添加的列显示后面的堆栈# ...
; 对于Python 2,它看起来有点不同,但相似.
在调试打印语句的列表周围使用薄包装可用于显示案例中的评估顺序:
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
.这个答案只是对那里提到的一般原则的补充.
归档时间: |
|
查看次数: |
3120 次 |
最近记录: |