1 常规的解包
Python的解包是非常实用且简洁的一个语法,其允许对多个变量同时赋值,这样的语法在其他编程语言中并不多见。最常用的解包语法如下:
a, b, c = 1, [2, 3], 4
a, b = b, a
上例中,首先利用解包同时对a、b、c三个变量赋值,其中,a与c分别被赋值为整数1和4,而b被赋值为一个list。随后利用解包语法实现了a、b两个变量的交换。
2 zip与for中的解包
zip函数与for配合可用于多个可迭代对象的并行迭代。zip函数的返回值也是一个list,这个list中的每一个元素都是一个tuple,故如果迭代变量也使用对应数量的多个,就可以利用解包性质,将多个tuple中的值同时赋值给多个变量:
for i, j in zip([1, 2], [3, 4]):
print(i, j)
上例中,zip([1, 2], [3, 4])的计算结果为[(1, 3), (2, 4)],这是一个长度为2的list,故for循环将循环两次。但由于迭代变量提供了两个,故每次迭代赋值时,迭代变量都进行了类似i, j = (1, 3)的解包赋值,从而将一个tuple解包为两个整形变量,从而可以在循环体中直接使用。
3 多重解包
解包时,赋值号左边的所有变量可以按照右值的数据结构进行嵌套解包。如一个嵌套了tuple和int的tuple:
(a, b), c = ((1, 2), 3)
上例中,首先对外层tuple进行解包,得到两个值,第一个值又是一个tuple,第二个值是一个int。然后,再对第一个值进行tuple到int的解包,再将一个tuple变成两个int,最终赋值给左边的三个变量。注意,进行多重解包时,左边变量的书写应遵循右边数据结构的分布,即int对应于int,tuple对应于tuple。
多重解包常用于多重zip与for连用,或复杂数据结构的zip。以下举例并不恰当,只是为了阐述多重解包的应用:
for (a, b), c in zip(zip(range(5), range(5,10)), range(10, 15)):
print(a, b, c)
上例中,使用多重解包并行迭代了嵌套的两个zip得到的list。显然,上例可简化为:
for a, b, c in zip(range(5), range(5, 10),range(10, 15)):
print(a, b, c)
4 函数参数的解包传递
函数的多个参数可以通过一个数据结构解包后同时传入,这在某些只能拿到序列形式的多个参数的情况下会很有用。
在传入函数实参时,在一个list(或tuple)前置星号,可对其进行解包的参数传入,即:
function(*[1, 2, 3])就相当于function(1, 2, 3)
注意,解包后相当于同时传入了三个参数,占据了三个参数位,而非一个list。
传参时如果对一个dict前置两个星号,则可以传入关键字参数,这种用法比较少见:
function(**{'a' : 1, 'b': 2})就相当于function(a = 1, b = 2)
5 函数的不定长参数声明
不定长参数在实际使用中并不常用,本文只对其作简要讨论。
Python的函数形参支持不定长参数声明。而由于Python对于实参的传入支持位置参数和关键字参数两种,所以相对应的,不定长参数的声明也包含位置参数和关键字参数的不定长声明两种。
在一个形参变量前置星号,可以将此变量声明为不定长位置参数,此参数自动转为tuple类型,且此参数必须声明在参数列表的末尾(不定长关键字参数之前):
def t(a, *b):
print(a, b)
t(1, 2, 3)
则这里a占用一个参数位,接受参数1,存为整形变量,b为不定长参数,接受剩余所有参数2、3,存为一个tuple。
在一个形参变量前置两个星号,可以将此变量声明为不定长关键字参数,此参数自动转为dict类型,且此参数必须声明在参数列表的末尾。这个dict的各个键值对分别为传入的关键字参数的名称和值:
def t(a, **b):
print(a, b)
t(1, b = 2, c = 3)
则在函数内部,a仍然接受整形1,而b是字典:{'b': 2, 'c': 3}。
6 Python3更广泛的解包与归并
Python3中,基于位置的解包与不定长参数归并语法并不只局限于函数传参,在赋值时也可使用:
a, *b, c = 1, 2, 3, 4 # b为[2, 3]
a, b, c, d = 1, *(2, 3), 4 #四个变量均能够正确赋值
2018年6月于苏州