多重赋值语义

70

在Python中可以这样做:

a, b   = 1, 2
(a, b) = 1, 2
[a, b] = 1, 2
我使用dis检查了生成的字节码,它们是相同的。
那么为什么要允许这样做呢?我是否需要其中之一而不是其他东西?

33
+1 用于检查生成的字节码 - Fábio Diniz
5个回答

75

当您需要让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')

5
更好的做法是使用 OrderedDict,这样变量 i 就会有更多的含义。 - infogulch
从Python 3.7开始,字典是有序的,因此不再需要有序字典。 - user1213898

13

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)

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


1
你认为在这种情况下允许使用方括号是一个bug吗?对我来说,这种行为似乎违反了(至少):“做一件事应该有一种——最好只有一种——显而易见的方法。”和“特殊情况并不足以打破规则。”谢谢。 - eat
1
@eat:我绝对不会称这为一个错误。它显然已经记录在案了。我只是想知道这个设计决策的理由。 - Sven Marnach
2
好的,这不是 bug,但当你说“只是想知道这个设计决策的基本原理”的时候,我也很好奇,并且不知道 [...]= 的一个(甚至单一的)合理使用情况是什么?谢谢。 - eat
1
方括号语法使得在枚举一系列集合时使用列表方法更加简单明了。不可否认,这是一个狭窄的用例,因为这种方式只能对等长的集合进行解包。 - Air

4

当解包单个元素的可迭代对象时,使用列表语法更加美观:

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

2

它们也是相同的,因为赋值是从右到左进行的,在右侧,你只有一种类型,即由两个元素组成的序列。当进行赋值调用时,该序列将被拆包并查找相应的元素以匹配并赋予这些值。

在这种情况下,任何一种方式都可以,将序列解包到相应的元素中即可。


你是在暗示这些情况下会有不同的工作方式吗? - Aillyn
在这种情况下,当LHS是单个标识符时,只会进行一次STORE_FAST调用。否则,它都是相同的。 - Senthil Kumaran

0

一个开括号允许多行赋值。例如,当从 csv.reader() 读取一行时,使用单个赋值将列表加载到命名变量中可以使代码更易读(如果不那么高效)。

从括号开始避免了长行或 \ 转义行。

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

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


1
你提到的低效率是什么? - Davis Herring
1
主要就是用那些变量名填充命名空间。我知道这并不是什么负担,但我来自嵌入式背景。 - verbamour
2
CPython将函数的局部变量编译成数组中的插槽;这可能比通过索引访问序列的元素(特别是反复访问)更有效。 - Davis Herring

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接