[Python]给函数传递不定个数的参数

浏览: 2601

问题起源

在Python中常常看到有这样的函数,它们的入口参数的个数是不定的。比如有如下代码

In: print zip([1, 2],[3, 4])
Out: [(1, 3), (2, 4)]
In: print zip([1, 2],[3, 4],[5, 6])
Out: [(1, 3, 5), (2, 4, 6)]

其中zip是Python自带的一个函数,其作用么,相信聪明的你已经看出来了。那如果有一天我们也要写一个能接受任意输入个数的python函数,该如何实现呢?

今天我就遇到这个问题,然后花了一点时间解决了——答案是,借助*这个符号。虽然这是个很基础的问题,网上资料很多,但还是想自己总结一下。

从求和函数谈起

我们不妨先定义一个灰常简单的函数——实现两个整数相加

def add_2_int(a1, a2):
   return a1+a2

一目了然,这就好比刚学编程时的print "hello world!"

不满足于此的我们,总想搞个大新闻——比如

实现多个整数的相加,但,事先不知道有几个整数。

这时我们就需要再学习一个——借助万能的*,来改写原函数:

def add_int(*a):
   s = 0
   for e in a:
       s += e    return s

我们先不管函数是怎么定义的、*a是什么含义,我们先看下怎么调用这个函数

In: print add_int(1, 2)
Out: 3 In: print add_int(1, 2, 3)
Out: 6

无论输入几个整数,都能求和,可见,这个函数已经能实现我们想要的功能了。

回过头看,这里调用add_int时输入参数是三个:1,2,3,但给add_int定义的输入是*a*的作用到底是什么呢?其实就是在调用函数时,把输入的三个参数1,2,3打包成一个元组(tuple),即打包成(1,2,3)a这个参数,为了证明这点,我们不妨在函数中间加一个print语句

def add_int(*a):
   s = 0
   print "input a is: ", a    for e in a:
       s += e    return s

调用函数并看下输出:

In: print add_int(1, 2, 3)
Out: input a is:  (1, 2, 3)     6

确实若我们所料。

这时我们脑洞开始变大了,如果我输入不是三个1,2,3,而是本身是一个列表[1,2,3],会不会也能实现求和呢?比如有如下代码

a_list = [1, 2, 3]print add_int(a_list)

啊哦,报错了:unsupported operand type(s) for +=: 'int' and 'list',说list不能跟int相加。我们分析一下,按照刚刚的理论,这里调用add_int时,只给了一个参数a_list,于是*a会将a_list打包成(a_list, )a,当我们执行for e in a语句时,得到的是a里面的每个元素,也就是a_list,而不是a_list里面的元素,所以显然无法得到想要的结果。

实现列表的连接

既然上面报错了,那我们索性将错就错,设计一个函数

实现任意多个list的『相加』——对于list而言+就是连接

我们只需修改函数add_int中用于存放累加结果的变量s即可,即:

def add_list(*a): 
   s = [] # 因为要返回list,所以s的初始值是[]而不是0
   for e in a:
       s += e    return s

比如我们要连接三个list[1,2],[3,4],[5,6],只需调用add_list([1, 2],[3, 4],[5, 6])即可得到[1, 2, 3, 4, 5, 6]


系铃和解铃

注意,上面我们说来说去,都是在函数定义阶段使用*来打包(系铃),使得函数能接受任意多的入口参数个数。现在遇到一个新问题:

给定两个整数求和函数(即上面的add_2_int(a1, a2)),现在有一个元组a_tuple = (1, 2),求1+2

暂时忘却刚说到的*,为了解决这个问题,一般我们会逐个取出a_tuple里的1,2,然后再调用函数,比如add_2_int(a_tuple[0], a_tuple[1])。有没有更简洁的方法呢?比如我们能否用某种手段使得输入的a_tuple自动拆开(解铃)成两个参数1, 2再输入函数呢?

答案是肯定的——还是用*,写成add_2_int(*a_tuple),注意,现在是函数调用阶段这里*的作用跟定义函数时恰恰相反,是将a_tuple(1,2)拆开成1, 2两个参数输入函数,对应函数的两个入口参数a1, a2 (扩展一下,这种函数的参数传递方式在Python中叫按位置传递,还有按关键字传递的方法,本文就不细说了)

解更多的铃

其实上面这个问题的想法来源很朴素——既然*可以在函数定义时把入口参数『打包』成tuple类型,那我在函数调用时,也可以将tuple类型的变量『解压』成一个一个的入口参数。那么问题就来了,延续上述问题,我把问题变成:

给定add_2_int(a1, a2),现在有一个列表a_list = [3, 4],一个集合a_set = set([6, 5]),求 3+45+6

不怕不怕,我们发现在解铃阶段(即函数调用阶段), *不光能拆开tuple类型,包括set,list等通通可以,也就是通通可以这样调用函数: add_2_int(*a_list),add_2_int(*a_set)

如果你脑洞再打开一点,之前我们用*定义了add_int(*a)函数,现在有a_list=[1,2,3],那么我们调用add_int(*a_list)会产生什么效果呢?

总结一下

本文简单实验了一下*在python函数中的作用,主要是区分函数定义和函数调用两个阶段,我把它称作系铃解铃
如果本文能对你编写python函数有所帮助,也不枉鄙人熬夜啦善莫大焉~

推荐 2
本文由 穆文 创作,采用 知识共享署名-相同方式共享 3.0 中国大陆许可协议 进行许可。
转载、引用前需联系作者,并署名作者且注明文章出处。
本站文章版权归原作者及原出处所有 。内容为作者个人观点, 并不代表本站赞同其观点和对其真实性负责。本站是一个个人学习交流的平台,并不用于任何商业目的,如果有任何问题,请及时联系我们,我们将根据著作权人的要求,立即更正或者删除有关内容。本站拥有对此声明的最终解释权。

0 个评论

要回复文章请先登录注册