解包
将容器里面的元素逐个取出来放在其他地方,在英文里叫做Unpacking,这个过程就是解包。Python中的解包是自动完成的。任何可迭代对象都支持解包,可迭代对象包括列表、元组、字典、集合、字符串、生成器等实现了next方法的一切对象。
示例,列表解包
1
2
3
4
5
6
7
|
l = [1,2,3,4]
a, b, c ,d = l
print(a) #输出1
print(b) #输出2
print(c) #输出3
print(d) #输出4
|
示例,元组解包
1
2
3
4
5
6
7
|
t = (1,2,3,4)
a, b, c ,d = t
print(a) #输出1
print(b) #输出2
print(c) #输出3
print(d) #输出4
|
示例,字符串解包
1
2
3
4
5
6
7
|
s = "abcd"
a, b, c ,d = s
print(a) #输出a
print(b) #输出b
print(c) #输出c
print(d) #输出d
|
示例,集合解包
1
2
3
4
5
6
7
|
s = {1,2,3,4}
a, b, c ,d = s
print(a) #输出1
print(b) #输出2
print(c) #输出3
print(d) #输出4
|
示例,字典解包
1
2
3
4
5
6
7
|
d = {"a":1, "b":2, "c":3, "d":4}
a, b, c ,d = d
print(a) #输出a
print(b) #输出b
print(c) #输出c
print(d) #输出d
|
字典解包后,只会把字典的key取出来,value则丢掉了。
示例,range解包
1
2
3
4
5
6
|
a, b, c ,d = range(4)
print(a) #输出0
print(b) #输出1
print(c) #输出2
print(d) #输出3
|
如果在解包过程中,遇到左边变量个数小于右边可迭代对象中元素的个数时该怎么办?
方法是在某个变量面前加一个星号,而且这个星号可以放在任意变量,每个变量都分配一个元素后,剩下的元素都分配给这个带星号的变量。
示例,带星号列表解包
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
l = [1,2,3,4]
*a1, b1, c1 = l
print(a1) #输出[1, 2]
print(b1) #输出3
print(c1) #输出4
a2, *b2, c2 = l
print(a2) #输出1
print(b2) #输出[2, 3]
print(c2) #输出4
a3, b3, *c3 = l
print(a3) #输出1
print(b3) #输出2
print(c3) #输出[3, 4]
|
示例,带星号元组解包
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
t = (1,2,3,4)
*a1, b1, c1 = t
print(a1) #输出[1, 2],解包后的类型是列表
print(b1) #输出3
print(c1) #输出4
a2, *b2, c2 = l
print(a2) #输出1
print(b2) #输出[2, 3],解包后的类型是列表
print(c2) #输出4
a3, b3, *c3 = l
print(a3) #输出1
print(b3) #输出2
print(c3) #输出[3, 4],解包后的类型是列表
|
示例,带星号字符串解包
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
s = "abcd"
*a1, b1, c1 = s
print(a1) #输出['a', 'b'],解包后的类型是列表
print(b1) #输出c
print(c1) #输出d
a2, *b2, c2 = s
print(a2) #输出a
print(b2) #输出['b', 'c'],解包后的类型是列表
print(c2) #输出d
a3, b3, *c3 = s
print(a3) #输出a
print(b3) #输出b
print(c3) #输出['c', 'd'],解包后的类型是列表
|
示例,带星号集合解包
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
s = {1,2,3,4}
*a1, b1, c1 = s
print(a1) #输出[1, 2],解包后的类型是列表
print(b1) #输出3
print(c1) #输出4
a2, *b2, c2 = s
print(a2) #输出1
print(b2) #输出[2, 3],解包后的类型是列表
print(c2) #输出4
a3, b3, *c3 = s
print(a3) #输出1
print(b3) #输出2
print(c3) #输出[3, 4],解包后的类型是列表
|
示例,带星号字典解包
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
d = {"a":1, "b":2, "c":3, "d":4}
*a1, b1, c1 = d
print(a1) #输出['a', 'b'],解包后的类型是列表
print(b1) #输出c
print(c1) #输出d
a2, *b2, c2 = d
print(a2) #输出a
print(b2) #输出['b', 'c'],解包后的类型是列表
print(c2) #输出d
a3, b3, *c3 = d
print(a3) #输出a
print(b3) #输出b
print(c3) #输出['c', 'd'],解包后的类型是列表
|
示例,带星号range解包
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
*a1, b1, c1 = range(4)
print(a1) #输出[0, 1],解包后的类型是列表
print(b1) #输出2
print(c1) #输出3
a2, *b2, c2 = range(4)
print(a2) #输出0
print(b2) #输出[1, 2],解包后的类型是列表
print(c2) #输出3
a3, b3, *c3 = range(4)
print(a3) #输出0
print(b3) #输出1
print(c3) #输出[2, 3],解包后的类型是列表
|
示例,利用解包,读取文件内容。获取文件的第一行和最后一行
1
2
3
4
|
with open('use_python_to_profit.txt') as f:
first, *_, last = f.readlines() # 注意,这会读取所有内容到内存中
print('first:', first)
print('last:', last)
|
函数参数的解包
函数调用时,有时你可能会用到两个符号:星号*和双星号**。函数被调用的时候,使用星号*解包一个可迭代对象作为函数的参数。字典对象,可以使用两个星号,解包之后将作为关键字参数传递给函数
示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
def func(a,b,c):
print(a,b,c)
#正常调用函数,必须传入三个参数,和形参a,b,c个数一只。
func(1,2,3) #输出1,2,3
#带星号列表解包函数参数
func(*[1,2,3]) #输出1,2,3
#带星号元组解包函数参数
func(*(1,2,3)) #输出1,2,3
#带星号字符串解包函数参数
func(*"abc") #输出a b c
#带星号字典解包函数参数
func(*{"a":1,"b":2,"c":3}) #输出a b c
#字典对象,可以使用两个星号,解包之后将作为关键字参数传递给函数
func(**{"a":1,"b":2,"c":3}) #输出1 2 3
|
看到了吗?和上面例子的区别是多了一个星号,结果完全不一样,原因是什么? 答案是**符号作用的对象是字典对象,它会自动解包成关键字参数 key=value 的格式:
1
2
|
>>> func(a=1,b=2,c=3)
1 2 3
|
如果字典对象中的 key 不是 a,b,c,会出现什么情况?
1
2
3
4
5
|
>>>func(*:{'a':1,'b':2,'d':3})
File "main.py", line 7
func(*:{'a':1,'b':2,'d':3})
^
SyntaxError: invalid syntax
|
总结一下,函数调用时,一个星号可作用于所有的可迭代对象,称为迭代器解包操作,作为位置参数传递给函数,两个星号只能作用于字典对象,称之为字典解包操作,作为关键字参数传递给函数。使用 *和 ** 的解包的好处是能节省代码量,使得代码看起来更优雅,不然你得这样写:
1
2
3
|
>>> d = {"a":1, "b":2, "c":3}
>>> func(a = d['a'], b=d['b'], c=d['c'])
1 2 3
|
到这里,解包还没介绍完,因为 Python3.5,也就是 PEP 448 对解包操作做了进一步的扩展, 在 3.5 之前的版本,函数调用时,一个函数中解包操作只允许一个* 和 一个**。从 3.5 开始,在函数调用中,可以有任意多个解包操作,例如:
1
2
3
4
5
6
|
# Python 3.4 中 print 函数 不允许多个 * 操作
>>> print(*[1,2,3], *[3,4])
File "<stdin>", line 1
print(*[1,2,3], *[3,4])
^
SyntaxError: invalid syntax
|
再来看看 python3.5以上版本
1
2
3
|
# 可以使用任意多个解包操作
>>> print(*[1], *[2], 3)
1 2 3
|
从 3.5开始可以接受多个解包,于此同时,解包操作除了用在函数调用,还可以作用在表达式中。
1
2
3
4
5
6
7
8
|
>>> *range(4), 4
(0, 1, 2, 3, 4)
>>> [*range(4), 4]
[0, 1, 2, 3, 4]
>>> {*range(4), 4}
{0, 1, 2, 3, 4}
>>> {'x': 1, **{'y': 2}}
{'x': 1, 'y': 2}
|
新的语法使得我们的代码更加优雅了,例如拼接两个列表可以这样:
1
2
3
4
|
>>> list1 = [1,2,3]
>>> list2 = range(3,6)
>>> [*list1, *list3]
[1, 2, 3, 3, 4, 5]
|
可不可以直接用+操作呢?不行,因为 list 类型无法与 range 对象 相加,你必须先将 list2强制转换为 list 对象才能做+操作,这个留给读者自行验证。
再来看一个例子:如何优雅的合并两个字典
1
2
3
4
|
>>> a = {"a":1, "b":2}
>>> b = {"c":3, "d":4}
>>> {**a, **b}
{'a': 1, 'b': 2, 'c': 3, 'd': 4}
|
在3.5之前的版本,你不得不写更多的代码:
1
2
3
4
5
6
|
>>> import copy
>>>
>>> c = copy.deepcopy(a)
>>> c.update(b)
>>> c
{'a': 1, 'b': 2, 'c': 3, 'd': 4}
|
函数返回值的解包
函数中如果返回多个结果,可以利用解包进行接收。
示例,有多个返回结果的函数
1
2
3
4
5
|
def func(x,y,z):
print(x,y,z)
return ['x',x],('y',y),('z',z)
res1=func('fir','sec','thr')
print(res1) #输出 (['x', 'fir'], ('y', 'sec'), ('z', 'thr'))。没有解包。
|
执行结果如下:
1
2
|
fir sec thr
(['x', 'fir'], ('y', 'sec'), ('z', 'thr'))
|
解包
1
2
3
4
5
6
7
|
def func(x,y,z):
print(x,y,z)
return ['x',x],('y',y),('z',z)
res1,res2,res3=func('fir','sec','thr')
print(res1) #输出 ['x', 'fir']
print(res2) #输出 ('y', 'sec')
print(res3) #输出 ('z', 'thr')
|
执行结果如下:
1
2
3
4
|
fir sec thr
['x', 'fir']
('y', 'sec')
('z', 'thr')
|
带星号解包
1
2
3
4
5
6
|
def func(x,y,z):
print(x,y,z)
return ['x',x],('y',y),('z',z)
res1,*res2=func('fir','sec','thr')
print(res1) #输出 ['x', 'fir']
print(res2) #输出 [('y', 'sec'), ('z', 'thr')]
|
执行结果如下:
1
2
3
|
fir sec thr
['x', 'fir']
[('y', 'sec'), ('z', 'thr')]
|
转载请注明本网址。