解包

将容器里面的元素逐个取出来放在其他地方,在英文里叫做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')]

转载请注明本网址。