Python赋值运算符优先级 - (a, b) = a[b] = {}, 5

35

我在Twitter上看到了这段Python代码片段,但它的输出结果让我感到困惑:

>>> a, b = a[b] = {}, 5
>>> a
{5: ({...}, 5)}

这里发生了什么?

1个回答

29

来自赋值语句文档

一个赋值语句会首先计算表达式列表(请记住,这可以是单个表达式或逗号分隔的列表,后者产生一个元组),然后将单个的结果对象分别赋值给目标列表,从左到右。

你有两个赋值目标列表;a, ba[b],值为{}, 5 的元组被分别从左到右分配给这两个目标。

首先将{}, 5元组解包到a, b。现在你有a = {}b = 5。注意{}是可变的。

接下来,将同样的字典和整数分配给a[b],其中a求值为字典,而b求值为5,因此你正在将键5设置为元组({}, 5),从而创建循环引用。因此,{...}指向与a已经引用的相同对象。

由于赋值按从左到右的顺序进行,因此您可以将其分解为:

a, b = {}, 5
a[b] = a, b

因此,a[b][0]a是相同的对象:

>>> a, b = {}, 5
>>> a[b] = a, b
>>> a
{5: ({...}, 5)}
>>> a[b][0] is a
True

4
哦,这对 Python 来说出人意料的不直观!我本以为它会从右到左级联,从而引发 NameError,而 a[b] = a, b = {}, 5 则能够工作。 - Claudiu
2
@Claudio:这个顺序仅适用于赋值是表达式并且有返回结果的语言。这强制执行从右到左的顺序。在Python中,赋值是一个语句,遵循自然阅读顺序。 - Martijn Pieters
1
@SunQingyao:你是否期望在赋值时 {}, 5 被评估两次?为什么会这样呢? {} 仍然是同一个对象。这相当于 d = {}; a, b = d, 5; a[b] = d, 5 - Martijn Pieters
1
@MartijnPieters 我认为应该像这样 tmp0,tmp1 = {},5; a,b = tmp0,tmp1; a [b] = tmp0,tmp1,这样就不会有循环引用了。 - nalzok
2
@SunQingyao:没错。将元组分配给字典中的一个键,你就创建了一个对自身的引用。 - Martijn Pieters
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接