【问题标题】:What is this kind of assignment in Python called? a = b = TruePython中的这种赋值叫什么? a = b = 真
【发布时间】:2012-07-14 22:54:17
【问题描述】:

我知道tuple unpacking,但是如果你在一行上有多个等号,这个作业叫什么?啦a = b = True

它总是让我有点不适应,尤其是当 RHS 是可变的时,但我在找到正确的关键字以在文档中搜索时遇到了真正的麻烦。

【问题讨论】:

标签: python variable-assignment


【解决方案1】:

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

- 请给我打鼓吗?

链式赋值


我只是在谷歌上运行了一下,发现关于这个主题的阅读内容并不多,可能是因为大多数人觉得它使用起来非常简单(而且只有真正的极客想了解更多关于主题)。


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

b = True
a = b

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

temporary_expr_result = True

a = temporary_expr_result
b = temporary_expr_result

在 stackoverflow 上可以进一步阅读:

【讨论】:

  • 真正的罪魁祸首是这种“链式分配”。如果a = b = c 意味着b = ca = b 之前执行,它将被链接分配,但正如您所展示的那样,情况并非如此。而是按顺序进行分配。
  • 这阐明了在像x = dictionary['x'] = value 这样的情况下,字典不使用set then get(如果它扩展到dictionary['x'] = value; x = dictionary['x'] 则会这样做)而是只设置字典值。
【解决方案2】:

使用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 的赋值与另一个线程之间可能发生线程切换查看他们两个可以看到两个变量中的不同值。

documentation, 7.2. Assignment statementsgh 是两个目标列表x表达式列表

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

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

【讨论】:

  • 此从左到右的评估是否有任何文档可用?
  • @AshwiniChaudhary 是的,我已经添加了文档中的相关摘录。
【解决方案3】:

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

The Wikipedia articlesenderle 链接到说:

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

另一个blog post 说:

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

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

赋值语句计算表达式列表 ... 并赋值 每个目标列表的单个结果对象,从左到 对。

因此,它并不是真正“从最右到左评估”——RHS 被评估,然后从最左边的目标分配到右——不是我能想到的任何真实世界(甚至是人为的)例子会有所作为。

【讨论】:

  • 我的描述是从开发人员的角度出发的,正如您自己所说的那样:没有 真实世界 的场景,其中的措辞会导致不同的结果。或者好吧,现在我实际上可以想到一个.. 等等,我会用一点 sn-p 来更新我的答案来解释我的意思。
  • 没关系,这没有任何意义(如果没有大量的 python hack 就无法完成,这超出了问题的范围),尽管我会用来自的一些信息来更新我的答案你的帖子。
  • 请记住,这不像一个普通的论坛,您可以用新的答案回复以前的帖子,如果您对某个帖子中的内容有意见,请使用“评论”功能,引用如果稍后删除/更改某个帖子,则您帖子中的另一个答案可能会造成麻烦。
  • @refp 我知道如何使用 Stack Overflow。我没有回复您的答案,它促使我进行更多挖掘并找到自己的答案。上次我检查这是正确的做事方式 - blog.stackoverflow.com/2011/07/…
  • 显示顺序的人为示例可能很重要:x = [0, 1, 2, 3]; x[0:2] = x[1:3] = [4]; 结果为 [4, 4],如果您反转它们,则结果为 [4, 3]
【解决方案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++ 中运行良好的原因。

【讨论】:

  • 一样!我认为依赖这种排序不是pythonic(我对“棘手”感兴趣,而不是确定它会起作用),任何读者都不会100%这样做。在这种情况下最好是明确的。
  • pythonic 的方法是使用元组分配/解包(如果我正确阅读了您的预期代码):tail, tail.next = tail.next, l1。 RHS 得到全面评估,然后从左到右分配到 LHS 元组中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-02-24
  • 1970-01-01
  • 2017-05-16
  • 2015-11-14
  • 2020-09-13
  • 2014-03-05
  • 2011-04-08
相关资源
最近更新 更多