1.6.1 封包和解包
封包:将多个值赋给同一个变量 或 return返回多个对象 的时候,python会将这多个值封装成一个元组,python的这个特性称之为 封包。
定义函数的时候,如果使用了 **kwargs 来接收多个自定义关键字参数,在实参调用的时候,会将这些自定义kv实参封装成字典。
# python的封包特性 a = 1,2,3,4 print(type(a))
解包:将一个可迭代对象赋值给多个变量的时候,python会将可迭代对象的元素按照位置依次赋值给对应位置的变量,如果可迭代对象的元素个数大于变量个数,可将 某个变量的前面加上*,这样该变量会接收多余的参数并组成一个列表。python的这个特性称之为 解包。
1)第一种解包( 可迭代对象赋值 ,可用 * 接收多个值)
# python的解包特性 # 当可迭代对象的长度和变量个数相等的时候,顺序拆分 a, b, c, d = (1,2,3,4)
# 当可迭代对象长度大于变量个数的时候,可在变量前面加上*,作用类似于可变长参数,这样解包的时候会 首先匹配其它位置的元素,然后将剩余元素组成列表,然后赋值给带星号(*)的变量 a,b,*c,d = (1,2,3,4,5) print(c) # [3,4] # 将可迭代对象解包给一个变量,需要后面加上 逗号 *k, = (1,2,3,4) pirnt(k)
2) 第二种解包 ( 函数传参, )
2.1)单星号 * 传参 (将 可迭代对象 解包为位置参数,如果可迭代对象为 字典,则单星号 *只会拆包到字典的 key)
# 单星号会将可迭代对象解包为 位置参数 def fun(a, b, c, d): return a, b, c, d # 封包 lst = [1,2,3,4] # 错误的调用方式 print( type(fun(lst)) ) # 正确的调用方式 print( type(fun(*lst)) )
在上述案例中,自定义函数fun 需要四个必须参数,lst中虽然有四个元素,但是直接将 lst 传入,实际只占据一个形参的位置,所以会报错。如果将【*可迭代对象名称】格式的参数 *lst 传入,会将可迭代对象lst解包为4个元素,分别赋值给四个形参,所以不会报错。
注:第一种解包方式和第二种解包方式都用 * 的含义区别:第一种解包方式,* 用来将变量接收多个值并生成列表。第二种解包,*表示 在传递实参的时候,将 可迭代对象解包为位置参数。
2.2)双星号 ** 传参(将 字典 解包为 关键字参数)
# 第二种解包, 函数传参 - 关键字参数 def fun(a,b,c): return a,b,c d = {"a": 1, "b": 2, "c": 3} # fun的正确调用方式 print( type( fun(**d) ) )
1.6.2 函数传参案例解析,分析下面两个案例的打印结果
1 # 函数传参案例一 2 def fun1(*a): # 在形参中,* 表示 ? 3 print(a) 4 print(*a) 5 6 a = (1,2,3) 7 fun1(*a) # 在实参中,* 表示 解包可迭代对象 变为 位置参数 8 9 10 # 函数传参案例二 11 def fun2(**a): # 在形参中,** 表示 ? 12 print(a) 13 print(**a) 14 15 a = {"a" : 1} 16 fun2(**a) # 在实参中,** 表示 解包字典 变为 关键字参数
案例解析一:
3th 定义了一个函数,7th 初始化一个可迭代对象,8th 调用了函数fun1,调用的时候传递实参 *a,会将可迭代对象a解包为位置参数 1 2 3;在自定义函数fun1中,函数头中的*a表示接收不定长参数,因为传过来的是多个实参,多个实参都要传给同一个变量a,因此python会将其封包为一个元组然后赋值a。故 在函数体内,a是一个封包后的元组,因此:在函数体内,4th的输出结果为一个元组:(1,2,3);5th 的输出结果是 将元组解包后的结果 :1 2 3 .
案例解析二:
13th 定义了一个函数,17th 初始化一个字典, 18th 调用了函数fun2,调用的时候传递实参 **a , 会将字典a解包为 关键字参数;因此,18th 等效于调用 fun2(a=2)。现在分析 13th:**a 接收自定义命名的关键字参数,并存储为一个字典。因此:14th的输出:{“a”: 1};对于 15th:调用了print()函数,函数在调用的时候,实参 **a 会将 字典a 解包为调用函数的关键字参数,但是print()函数并没有 参数名为a 的参数,因此,程序执行到这一样的时候会报错!!