多重赋值语义

Posted

技术标签:

【中文标题】多重赋值语义【英文标题】:Multiple assignment semantics 【发布时间】:2011-07-08 03:25:30 【问题描述】:

在 Python 中可以做到:

a, b   = 1, 2
(a, b) = 1, 2
[a, b] = 1, 2

我使用dis检查了生成的字节码,它们是相同的。 那么为什么要允许这样做呢?我会需要其中一个而不是其他吗?

【问题讨论】:

+1 用于检查生成的字节码 【参考方案1】:

您需要在赋值左侧包含更多结构的一种情况是,当您要求 Python 解包一个稍微复杂一点的序列时。例如:

# Works
>>> a, (b, c) = [1, [2, 3]]

# Does not work
>>> a, b, c = [1, [2, 3]]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: need more than 2 values to unpack

这在过去证明对我很有用,例如,当使用 enumerate 迭代一个 2 元组序列时。比如:

>>> d =  'a': 'x', 'b': 'y', 'c': 'z' 
>>> for i, (key, value) in enumerate(d.iteritems()):
...     print (i, key, value)
(0, 'a', 'x')
(1, 'c', 'z')
(2, 'b', 'y')

【讨论】:

更好:使用 OrderedDict 和 i 将具有更多意义。 从 python 3.7 开始,字典是(插入)有序的,因此不需要有序的字典。【参考方案2】:

Python 元组通常可以带括号也可以不带括号:

a = 1, 2, 3

等价于

a = (1, 2, 3)

在某些情况下,您需要括号来解决歧义,例如,如果要将元组(1, 2) 传递给函数f,则必须编写f((1, 2))。因为有时需要括号,所以它们总是允许保持一致,就像你总是可以写 (a + b) 而不是 a + b

如果要解包嵌套序列,还需要括号:

a, (b, c) = 1, (2, 3)

似乎没有理由也允许使用方括号,而且人们很少这样做。

【讨论】:

如果在这种情况下允许使用方括号,您会认为这是一个错误吗?对我来说,这种行为似乎违反了(至少):There should be one-- and preferably only one --obvious way to do it.Special cases aren't special enough to break the rules. 谢谢 @eat:我绝对不会称之为错误。显然是documented。我只是想知道这个设计决定的基本原理。 好吧,不是错误,但是当你说just wondering about the rationale for this design decision 时,我也对[...]= 的(甚至单个)合理用例感到好奇和困惑?谢谢 方括号语法使得在枚举集合序列时使用列表方法更加直接。诚然,这是一个狭窄的用例,因为以这种方式解包的集合必须具有相同的长度。【参考方案3】:

解压单元素可迭代对象时,列表语法更漂亮:

a,=f()    # comma looks out of place
(a,)=f()  # still odd
[a]=f()   # looks like every other list

【讨论】:

【参考方案4】:

它们也是相同的,因为赋值发生在从右到左和在右边,你有一个类型,它是两个元素的序列。当进行赋值调用时,序列被解包并寻找相应的元素以匹配并赋予这些值。 是的,在这种情况下,将序列解包到相应的元素中,任何一种方式都应该没问题。

【讨论】:

您是否暗示在某些情况下它们的工作方式会有所不同? 在这种情况下,当 LHS 是单个标识符时,则只进行一次 STORE_FAST 调用。否则都是一样的。【参考方案5】:

左括号允许多行赋值。例如,当从csv.reader() 中读取一行时,通过一次赋值将列表加载到命名变量中会使代码更具可读性(如果效率较低)。

以括号开头可避免长或\ 转义行。

(a, b,
c) = [1, 2, 3]

(想象更多更长的变量名)

【讨论】:

你提到的低效率是什么? 主要是用这些变量名填充命名空间。我知道这不是什么负担,但我来自嵌入式背景。 CPython 将函数的局部变量编译到数组中的槽中;它可能比通过索引(尤其是重复)访问序列的元素更有效。

以上是关于多重赋值语义的主要内容,如果未能解决你的问题,请参考以下文章

Ruby 如何在语义上处理赋值?

Python中的同时赋值语义

对象赋值的语义

具有赋值语义的非所有持有者

复制 ctor 和赋值运算符中是不是存在语义稍有不同的问题?

cpp►C++11右值引用移动语义移动构造函数移动赋值运算符