解释整数 x, item in list[x] 的单行赋值和相互引用变量的单行赋值之间的区别

Explain the difference between single-line assignment of integer x, item in list[x], and single-line assignments of inter-referencing variables

我相信我在试图弄清楚如何正确提问时回答了我自己的问题,但由于我发现 Python's documentation 中的答案相当不透明,我想我会还是post这里的问题。

在尝试理解 Python 中的单行赋值规则时,我遇到了一些与 "single-line assignments to multiple variables all take place at once".

的通常说法相矛盾的例子
a = 0 # variable 'a' must be assigned first

a, b = 1, a # a, b :: 1, 0

x = [2, 3, 4, 5, 6]

i, x[i] = 4, 99 # i, x[4] :: 4, 99; variable 'i' does not need to have been previously assigned

c = 8 # variable 'c' must be assigned first

c, d = 9, print(c) # c, d :: 9, None; prints 8

我的困惑与以下事实有关:Python 在将 99 分配给列表 'x' 的索引 4 之前首先重新分配了 'i'(即列表索引)。

虽然 Python 的文档如下直接解决了这个问题,

Although the definition of assignment implies that overlaps between the left-hand side and the right-hand side are ‘simultaneous’ (for example a, b = b, a swaps two variables), overlaps within the collection of assigned-to variables occur left-to-right, sometimes resulting in confusion.

没看懂"overlaps within the collection of assigned-to variables"是什么意思。

我现在看到解析器将检查给定列表的索引值是否已重新分配,然后再将新值分配给列表的索引。


备注:

事实证实了这一点,在这种情况下,'i'在用作变量索引之前不需要先赋值,而对于'a'则有必要(否则, Python 会抛出错误)。

对于那些好奇的人,这里是 PythonTutor visualization。不幸的是,因为它在一行中执行赋值(它应该),所以人们无法真正看到 Python 如何解释该语句。

如果用户之前将 i 分配给一个整数,并打算使用之前的整数作为索引,而不是新值,那么这种执行的不透明性将进一步加剧。


顺便说一句,这是我第一次提问,所以我没有 post 自己回答的必要声誉。请随时就我如何改进我可能提出的任何未来问题或我如何更好地为这个社区做出贡献提供任何建设性的建议。谢谢

也许这是一道英文题?

overlaps within the collection of assigned-to variables occur left-to-right

所以,LHS 是 "the collection of assigned-to variables"。换句话说,"assigned-to variables" 是将被赋值的变量,因此是 LHS。

"Overlaps within LHS"表示一个变量所指的对象依赖于另一个。在您的示例中,c[i] 引用的对象取决于 i,因此

i, c[i] = 4, 99

从左到右是运行。

然而,在接下来的测试中:

c, d = 9, print(c)

由于LHS变量cd不重叠(相互依赖),所以对cd的赋值是并行的。

基本上是说,如果在赋值语句的左侧,您有重复的值,在本例中 i,那么赋值将需要放置 left-to-right,因此您从 left-most 值 (i) 开始,分配它,然后分配下一个 (x[i] ), 依此类推

编辑:@iBug 说得更好。

考虑这一点的最简单方法是首先评估右侧 (RHS),然后将其分配给左侧 (LHS)。

注意,当RHS中有逗号时,就是actually a tuple。当LHS为元组或列表时,则将其视为特殊语法,其中RHS将被解包并分配给LHS上的变量。

所以当LHS是元组或变量列表时,过程是这样的:

  1. 构造RHS,从元组的第一个元素开始,一直到最后
  2. 解压 RHS 元组并将元素 one-by-one 分配给 LHS 上的对象,从左到右。

换句话说,幕后Python转

(a, b, c) = (x(), y(), z())

进入

buffer = (x(), y(), z())
a = buffer[0]
b = buffer[1]
c = buffer[2]

上还有一些关于此的更多信息。

请注意,这在有或没有括号的情况下都有效(您不需要括号来创建元组),并且它适用于 LHS 上的元组或列表以及 RHS 上的任何可迭代对象。此外,在 LHS 上命名的变量不必事先存在(这是 LHS 永远不会组装成普通元组的明显标志)。