序列解包

赋值语句你见过很多,有的给变量赋值,还有的给数据结构的一部分(如列表中的元素和切片,或者字典项)赋值,但还有其他类型的赋值语句。例如,可同时(并行)给多个变量赋值:

>>> x, y, z = 1, 2, 3
>>> print(x, y, z)
1 2 3

看似用处不大?看好了,使用这种方式还可交换多个变量的值。

>>>> x, y = y, x
>>>> print(x, y, z)
>2 1 3

实际上,这里执行的操作称为序列解包(或可迭代对象解包):将一个序列(或任何可迭代对象)解包,并将得到的值存储到一系列变量中。下面用例子进行解释。

>>> values = 1, 2, 3
>>> values
(1, 2, 3)
>>> x, y, z = values
>>> x
1

这在使用返回元组(或其他序列或可迭代对象)的函数或方法时很有用。假设要从字典中随便获取(或删除)一个键-值对,可使用方法popitem,它随便获取一个键-值对并以元组的方式返回。接下来,可直接将返回的元组解包到两个变量中。

>>> scoundrel = {'name': 'Robin', 'girlfriend': 'Marion'}
>>> key, value = scoundrel.popitem()
>>> key
'girlfriend'
>>> value
'Marion'

这让函数能够返回被打包成元组的多个值,然后通过一条赋值语句轻松地访问这些值。要解包的序列包含的元素个数必须与你在等号左边列出的目标个数相同,否则Python将引发异常。

>>> x, y, z = 1, 2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: need more than 2 values to unpack
>>> x, y, z = 1, 2, 3, 4
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: too many values to unpack

可使用星号运算符(*)来收集多余的值,这样无需确保值和变量的个数相同,如下例所示:

>>> a, b, *rest = [1, 2, 3, 4]
>>> rest
[3, 4]

还可将带星号的变量放在其他位置。

>>> name = "Albus Percival Wulfric Brian Dumbledore"
>>> first, *middle, last = name.split()
>>> middle
['Percival', 'Wulfric', 'Brian']

赋值语句的右边可以是任何类型的序列,但带星号的变量最终包含的总是一个列表。在变量和值的个数相同时亦如此。

>>> a, *b, c = "abc"
>>> a, b, c
('a', ['b'], 'c')

这种收集方式也可用于函数参数列表中。

链式赋值

链式赋值是一种快捷方式,用于将多个变量关联到同一个值。

x = y = somefunction()

上述代码与下面的代码等价:

y = somefunction()
x = y

请注意,这两条语句可能与下面的语句等价:

x = somefunction()
y = somefunction()

增强赋值

可以不编写代码x = x + 1,而将右边表达式中的运算符(这里是+)移到赋值运算符(=)的前面,从而写成x += 1。这称为增强赋值,适用于所有标准运算符,如*/%等。

>>> x = 2
>>> x += 1
>>> x *= 2
>>> x
6

增强赋值也可用于其他数据类型(只要使用的双目运算符可用于这些数据类型)。

>>> fnord = 'foo'
>>> fnord += 'bar'
>>> fnord *= 2
>>> fnord
'foobarfoobar'

通过使用增强赋值,可让代码更紧凑、更简洁,同时在很多情况下的可读性更强。