44

我知道元组拆包,但是在单行上有多个等号的情况下,这个作业叫什么?啦啦a = b = True

它总是让我有点不适应,尤其是当 RHS 是可变的时,但我很难找到正确的关键字来在文档中搜索。

4

4 回答 4

71

这是一个任务链,用来描述它的术语是……

- 请给我打鼓好吗?

链式分配


我只是给它一个相当谷歌的运行,发现关于这个主题没有太多可阅读的内容,可能是因为大多数人发现它使用起来非常简单(只有真正的极客想了解更多关于这个主题的信息) .


在前面的表达式中,计算顺序可以被视为从最右边开始,=然后向左工作,这相当于写作:

b = True
a = b

上面的顺序是大多数语言描述的assignment-chain,但 python 的做法不同。在 python 中,表达式被评估为下面的等价物,尽管它不会产生任何其他结果,而不是前面描述的结果。

temporary_expr_result = True

a = temporary_expr_result
b = temporary_expr_result

在 stackoverflow 上可以进一步阅读:

于 2012-07-16T05:15:37.073 回答
13

dis使用(反汇编)模块此输出进一步支持@refp 的答案:

>>> def a(x):
...   g = h = x
...
>>> import dis
>>> dis.dis(a)
  2           0 LOAD_FAST                0 (x)
              3 DUP_TOP
              4 STORE_FAST               1 (g)
              7 STORE_FAST               2 (h)
             10 LOAD_CONST               0 (None)
             13 RETURN_VALUE

RHS 被检索和复制,然后从左到右存储到目标变量中(自己试试这个e = f = g = h = x)。

如果 RHS 是一个函数调用,其他一些海报会感到困惑,例如a = b = fn()- RHS 只评估一次,然后将结果分配给每个连续的变量。如果返回的值是可变的,例如列表或字典,这可能会导致不必要的共享。

对于那些使用的人threading来说,注意在多个显式赋值语句上链式赋值形式没有隐含的“原子性”是有用的——在 g 和 h 的赋值之间可能发生线程切换,另一个线程查看它们中的两个可以在两个变量中看到不同的值。

文档中,7.2。赋值语句,作为两个目标列表g,作为表达式列表:hx

assignment_stmt ::=  (target_list "=")+ (expression_list | yield_expression)

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

于 2012-07-16T07:35:46.967 回答
9

好的,“链式分配”是我所追求的搜索词,但经过更多挖掘后,我认为它并不完全正确。但它比“赋值语句的特殊情况”更容易搜索。

链接到的维基百科文章 senderle说:

在 Python 中,赋值语句不是表达式,因此不返回值。相反,链式赋值是一系列语句,单个表达式具有多个目标。赋值是从左到右执行的,因此i = arr[i] = f() 计算表达式f(),然后将结果分配给最左边的目标 ,i然后 arr[i]使用 的新值将相同的结果分配给下一个目标i

一篇博文说:

在 Python 中,赋值语句不返回值。链式赋值(或者更准确地说,看起来像链式赋值语句的代码)被识别和支持为赋值语句的一个特例。

这对我来说似乎是最正确的,仔细阅读文档- 特别是 (target_list "=")+- 这也说

赋值语句计算表达式列表 ... 并将单个结果对象从左到右分配给每个目标列表。

所以它并不是真正“从最右到左评估”——RHS被评估,然后从最左边的目标分配到右——不是我能想到任何现实世界(甚至是人为)的例子,它会做一个不同之处。

于 2012-07-16T06:05:04.990 回答
4

由于我的无知,今天被python的Chained Assignment咬了。在代码中

if l1.val <= l2.val:
    tail = tail.next = l1 # this line 
    l1 = l1.next

我所期望的是

tail.next = l1
tail = tail.next
# or equivalently 
# tail = l1

而我在下面,它在列表中产生了一个自我循环,让我陷入无限循环,哎呀......

tail = l1
tail.next = l1 # now l1.next is changed to l1 itself

因为对于 a = b = c,一种方式(例如 python)相当于

tmp = evaluate(c)
evaluate(a) = tmp
evaluate(b) = tmp

并且对于两个赋值具有相等的右操作数。

另一个(例如 C++)等价于

evaluate(b) = evaluate(c)
evaluate(a) = evaluate(b)

因为在这种情况下a = b = c基本上是

b = c
a = b

并且两个右手操作数可能不同。

这就是为什么类似的代码在 C++ 中运行良好的原因。

于 2016-04-16T14:55:54.533 回答