python封装函数教学_Python函数参数的打包和解包(超级详细)

在前面的教程中,我们介绍了两种可变参数的标记方式:利用一个星号*构建一个参数元组;利用两个星号**构建参数字典。

事实上,在函数参数传递过程中,还有一种看似类似实则不然的参数传递方式。说它“类似”,是因为在外观上它也在参数前打上一个星号*。说它“不然”,是因为这种操作的内涵不同:星号*是作用在实参上的;实参是有讲究的,这些实参主要包括列表、元组、集合、字典及其他可迭代对象。

如果在这类实参前面加上一个星号*,那么 Python 解释器就会对这些可迭代对象进行解包(unpacking,亦有文献译作“解压”),然后将解包后的元素一一分配给多个形参。

Python参数打包

说到解包,我们先介绍一下它的反操作——打包(packing),参见如下代码:

In [1]: val = 1, 2, 3, 4

In [2]: type(val)

Out[2]: tuple

In [3]: val

Out[3]: (1, 2, 3, 4)

在输入 In [1] 处,表达式等号的右边分别是四个零散的整型数 1, 2, 3, 4,然后赋值给了 val 对象。通过元组知识的学习,我们知道,Python 将等号右边的四个整型数“打包”成了一个匿名的元组,然后赋值给 val。

另一方面,Python 中变量的类型并不需要事先声明,而是通过赋值得到的。通过赋值操作,将等号右边的变量类型赋给等号左边的对象即可。如此一来,In [1] 处 val 的类型就被定义为一个元组了。

上述判断可从 Out[2] 的输出结果中得到印证。在输出 Out[2] 中,元组的另外一个标志——那对圆括号( ),也被 Python 解释器自动加上了。

Python参数解包

现在的问题是,如果我们把元组作为一个整体给分散对象赋值,那么这个打包元组中的元素会被一一解析出来吗?延续前面变量 val 的赋值,请参考如下代码:

In [4]: a, b, c, d = val

In [5]: print(a, b, c, d)

1 2 3 4

In [6]: type (a)

Out[6]: int

从输入 In [4] 处可知,通过等号可将右侧的元组 val一一对应赋值给等号左侧的四个变量。在其他编程语言中,一对四的赋值方式通常是不被允许的。但在 Python 语法糖的包装下,上述方式是合法的。在 In [5] 处,通过 print( ) 输出验证,变量 a、b、c、d 的值均可正常输出。

在 In [6] 处,用全局函数 type( ) 测试 a 的类型,可以看出,a 的类型也是正确的 (int),并非 val 的元组类型。我们把这种将可迭代对象的元素分别赋值为分散对象的过程,称为解包。

关于解包,需要注意的有两点。

第一点:被解包的序列中的元素数量必须与赋值符号=左边元素的数量完全一样,否则就会报错。参见如下代码:

In [7]: val = 1,2,3 #val 为一个包含三个元素的元组

In [8]: a, b, c, d = val #将 val 解包给四个元素,错误!

-----------------------------------------------------------

ValueError Traceback (most recent call last)

in

------> 1 a, b, c, d = val

ValueError: not enough values to unpack (expected 4, got 3)

在 In [7] 处,元组 val 内包含三个元素,分别是 1、2、3。但在 In [8] 处,等号右侧有四个变量(分别是 a、b、c、d)等着被赋值,解包元素的数量不够!因此 Python 解释器会“毫不客气”地指出问题所在:没有足够的值来解包。

第二点:支持解包操作的不仅限于元组,也包括所有可迭代的对象,比如列表、字典等。

于是,我们想知道,这种自动解包的行为能否也在函数参数传递时发生?比如说,如果实参为一个列表或元组,它会自动解包,将其内的元素一一分配给不同的形参吗?想知道答案,请参看如下代码:

In [9]: def fun (a, b, c, d): #定义带有四个参数的函数 fun ()

...: print(a, b, c, d)

In [10]: my_list = [1, 2, 3, 4] #定义一个包括四个元素的列表

In [11]: fun(my_list) #以列表为实参调用fun (),发生错误!

Traceback (most recent call last):

File "n, line 1, in

fun(my_list)

TypeError: fun() missing 3 required positional arguments: 'b', 'c' and 'd'

上述代码的 In [9] 处定义了一个函数 fun( ),它有四个形参 a、b、c、d。然后 In [10] 处又定义了一个包含四个元素的列表 my_list。

我们原有的想法是,把 my_list 作为实参,通过解包操作给四个形参赋值,即分别让 a=1、b=2、c=3、d=4。但 In [11] 处的输出结果让我们失望了,Python 系统好像并不认可这种“简单粗暴”的参数解包行为。

那有没有办法让这种参数解包行为成功呢?其实,我们距成功仅一步之遥。类似于可变参数,只需要在可迭代对象前打上一个星号*,一切就都可以完美解决了,参见如下代码:

In [12]: fun(*my_list) #可迭代对象前的星号*不可缺少。

12 3 4

In [12] 处参数部分的那个星号*,其功能就是将可迭代对象(实参)的每个元素分解成一个个离散的参数,然后分别赋值给对应的形参。

列表可以这么解包,那如果可迭代对象是一个字典,又该如何解包呢?这时在可迭代的实参前添加一个星号*,是否依然可行呢?请参见如下代码:

In [13]: def fun(a, b, c):

...: print(a, b, c)

...:

In [14]: d = {'a':2, 'b':4, 'c':10}

In [15]: fun(*d)

a b c

通过上面的输出结果可以看出,程序运行正常,并无语法错误,但输出结果不是我们想要的,以上代码仅仅把形参名输出了。

那该如何修正呢?如同前文讲解的那样,对于由字典构成的可变参数,我们用两个星号**表示,这里对字典的解包,也需要在字典名称前加上两个星号**,示例代码如下:

In [16]: fun(**d) #在字典对象前加两个星号**,正确输出!

2 4 10

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值