Python中的同时赋值语义

11

考虑以下Python 3代码:

a = [-1,-1,-1]
i = 0

现在考虑同时对a和i进行两个版本的赋值:

赋值版本1:

a[i],i = i,i+1

作业版本 2:

i,a[i] = i+1,i

我希望这两个同时赋值的版本在语义上是等效的。然而,如果你检查每一个同时赋值后a和i的值,你会得到不同的结果:

版本1赋值后print(a,i)的输出:

[0, -1, -1] 1

第二个版本赋值后print(a,i)的输出结果:

[-1, 0, -1] 1

我不是Python语义学的专家,但这种行为似乎很奇怪。我希望两个赋值都像赋值版本1一样。此外,如果您检查以下链接,人们会期望两个赋值版本导致相同的状态:

Google Books中有关书籍摘录的链接

对于Python同时赋值的语义,我是否遗漏了什么?

注意:这种奇怪的行为似乎无法重现,例如当变量a具有整数类型时;它似乎需要a是列表类型(也许对于任何可变类型都是如此?)。

1个回答

12

在这种情况下:

i, a[i] = i + 1, i

右侧求值得到了一个元组 (1, 0)。然后将这个元组解包给 i,然后是 a[i]。在解包期间,a[i] 被计算,而不是在之前,所以对应于 a[1]

由于右侧在任何解包之前被评估,所以无论 i 的最终值如何,引用右侧的 a[i] 始终是 a[0]

这里还有另一个毫无意义的有趣例子供你解决。

>>> a = [0,0,0,0]
>>> i, a[i], i, a[i] = range(4)
>>> a
[1, 0, 3, 0]

谢谢gnibbler,我理解你提出的关于元组评估的技术点;从那个角度来看,一切都很合理。然而,从语义的角度来看,我仍然有些困惑,因为这样的话“同时”的赋值语义就不是非常“同时”了。由于我是Python的新手,我想冒险问一下是否有官方文档对Python的赋值语义进行了正式/半正式的定义。你知道有这样的文档吗? - hquilo
3
“同时赋值”一词在 Python 文档中并不存在。a, b = 1, 2 只是 (a, b) = (1, 2) 的简写,这被称为序列解包。 - Blender
谢谢 @gnibler 和 @Blender 两位;你们的回答让事情变得清楚了。 - hquilo
只是为了添加一个文档参考(https://docs.python.org/3/reference/simple_stmts.html):“尽管赋值的定义意味着左侧和右侧之间的重叠是‘同时’的(例如,`a,b = b,a`交换两个变量),但在分配给变量的集合内部发生的重叠从左到右发生,有时会导致混淆。” - Kurt Peek

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