转载:http://blog.csdn.net/u012102306/article/details/52250028 收藏参考
问题起源
在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+4
和5+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函数有所帮助,也不枉鄙人熬夜啦善莫大焉~