我知道元组拆包,但是在单行上有多个等号的情况下,这个作业叫什么?啦啦a = b = True
它总是让我有点不适应,尤其是当 RHS 是可变的时,但我很难找到正确的关键字来在文档中搜索。
我知道元组拆包,但是在单行上有多个等号的情况下,这个作业叫什么?啦啦a = b = True
它总是让我有点不适应,尤其是当 RHS 是可变的时,但我很难找到正确的关键字来在文档中搜索。
这是一个任务链,用来描述它的术语是……
- 请给我打鼓好吗?
我只是给它一个相当谷歌的运行,发现关于这个主题没有太多可阅读的内容,可能是因为大多数人发现它使用起来非常简单(只有真正的极客想了解更多关于这个主题的信息) .
在前面的表达式中,计算顺序可以被视为从最右边开始,=
然后向左工作,这相当于写作:
b = True
a = b
上面的顺序是大多数语言描述的assignment-chain,但 python 的做法不同。在 python 中,表达式被评估为下面的等价物,尽管它不会产生任何其他结果,而不是前面描述的结果。
temporary_expr_result = True
a = temporary_expr_result
b = temporary_expr_result
在 stackoverflow 上可以进一步阅读:
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
,作为表达式列表:h
x
assignment_stmt ::= (target_list "=")+ (expression_list | yield_expression)
赋值语句计算表达式列表(请记住,这可以是单个表达式或逗号分隔的列表,后者产生一个元组)并将单个结果对象分配给每个目标列表,从左到右。
好的,“链式分配”是我所追求的搜索词,但经过更多挖掘后,我认为它并不完全正确。但它比“赋值语句的特殊情况”更容易搜索。
在 Python 中,赋值语句不是表达式,因此不返回值。相反,链式赋值是一系列语句,单个表达式具有多个目标。赋值是从左到右执行的,因此
i = arr[i] = f()
计算表达式f()
,然后将结果分配给最左边的目标 ,i
然后arr[i]
使用 的新值将相同的结果分配给下一个目标i
。
另一篇博文说:
在 Python 中,赋值语句不返回值。链式赋值(或者更准确地说,看起来像链式赋值语句的代码)被识别和支持为赋值语句的一个特例。
这对我来说似乎是最正确的,仔细阅读文档- 特别是 (target_list "=")+
- 这也说
赋值语句计算表达式列表 ... 并将单个结果对象从左到右分配给每个目标列表。
所以它并不是真正“从最右到左评估”——RHS被评估,然后从最左边的目标分配到右——不是我能想到任何现实世界(甚至是人为)的例子,它会做一个不同之处。
由于我的无知,今天被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++ 中运行良好的原因。