本篇内容为基础部分,进阶部分请查看:谈妖:Python函数知识大杂烩(二)函数高级进阶zhuanlan.zhihu.com
写代码时,我们经常会遇到同一段代码可以在不同地方应用的情况。
比如说,小明的代码中有好几处重复,都是需要将前一段代码块产生的 x,y 两个结果进行相加,并且返回相加的值。
于是小明写开始写了:
z = x + y
虽然这个代码量不大,但还有好几段几十行的代码也出现了这样的情况,写了四五遍之后小明很烦。
怎么办呢?
这个时候函数就该登场了。Python中的函数是有若干语句加上函数名和参数列表组成的语句块,它可以封装一段代码,实现代码复用。
小明只需要定义一个函数,以后就可以通过函数名来完成调用,再也不用吭哧吭哧狂敲键盘了。
定义函数
想要定义一个函数非常简单,通过 def 语句就可以完成。
def xiaoming(x):
print('小明')
有了上面两行代码,小明已经成功定义了一个叫做 xiaoming 函数。
其中 'xiaoming'称为函数名,()小括号中的 x 被称为参数,第二行的 print 语句被称为函数体,最后还有一个隐藏的 Return 语句没有写,这种情况下Python会默认返回一个 None。
在定义完成之后,函数不会立即执行,只有在调用时才会执行。
我们来尝试一下调用这个函数:
>>> xiaoming(1)
小明
在函数名后加小括号,填入定义时规定的参数,我们就完成了函数的调用。
函数参数
函数的参数非常重要。这其中有几个我们必须掌握的概念,也是面试中经常考察的知识点:
形参、实参和传参形参全称形式参数,是我们在定义函数时加入的参数,在实际调用的时候,不一定会使用这个参数,小明函数中的 'x' 就是一个形式参数。
实参全称实际参数,是我们在调用函数时在括号内使用的参数,这个参数往往会在函数体中使用,我们调用小明函数时填入的 '1' 就是一个实际参数。
至于传参则是一个动作,我们把实参写入调用时,就是在将实际参数传递到函数中。
传入的实参必须与定义的形参个数相配,否则Python程序就会报错。
位置参数
当函数有位置参数时,我们可以按照参数定义顺序传入实参,Python程序会自动按照顺序分配,让函数得以执行。
>>> def func1(x,y,z):
... return x,y,z
...
>>> func1(1,2,3)
(1, 2, 3)
位置参数很好用,传参时只需要写入实参就可以调用,非常节省时间。
但是小明不满意呀,位置参数固然方便,但是万一我传参的时候把参数顺序弄混了,函数也不会报错,不就惨了?
关键字参数
当我们使用形参的名字来进行传参时,我们就引入了一个新的概念:关键字参数。
使用关键字参数时,传参时无需将位置一一对应,Python程序会根据形参来进行一一分配。
>>> func1(y=2,x=3,z=1)
(3, 2, 1)
可以看到实参的传入顺序不会影响最终的结果。
位置参数与关键字参数共存时,位置参数必须位于关键字参数之前,因为位置参数是严格按照位置一一对应的。
写出上面函数的小明还是不满意,在上一个函数中,y 的实参一直都是2,但每次调用都需要重新传入,这也太麻烦了吧?
于是我们引入一个新的概念:
默认参数
我们在定义函数时就给形参赋值,这样就出现了默认参数。参数的默认值可以在为传入足够参数时,将未传值的参数赋值成默认值,当参数非常多的时候,默认参数可以极大地简化函数调用。
我们可以这样改造一下func1:
>>> def func2(x,z,y=2):
... return x,y,z
...
>>> func2(1,3)
(1, 2, 3)
这下我们不用传入y的实参,函数也可以正常运行了。
结果小明又不满意了!
他觉得三个参数不够用,万一哪天需要放进去4个或者5个参数呢?
好吧,那我们就来学习一下可变参数!
可变参数
我们看下面这个函数:
>>> def func3(nums):
... sum = 0
... for x in nums:
... sum += x
... return sum
...
>>> func3([1,2,3])
6
在上面的函数中,只要我们传入一个可迭代对象,那么这个可迭代对象中的所有值都可以在函数中应用,实现了 nums 一个形参对应多个参数(而非实参,此处仅有一个实参[1,2,3])。
不想传入可迭代对象,我们还可以使用可变位置参数和可变关键字参数。
>>> def func4(*args,**kwargs):
... return args,kwargs
...
>>> func4(1,2,3,x=4,y=5,z=6)
((1, 2, 3), {'x': 4, 'y': 5, 'z': 6})
在形参前加一个'*'即是可变位置参数,加两个'**' 就是可变关键字参数,随后我们就可以随意传输相应个数的实参。
我们发现,传入的可变位置参数会被整理成元组(Tuple),而可变关键字参数怎会被整理成字典(Dict)。
实际的使用过程中,可变位置参数必须放在可变关键字参数之前。
keyword-olny参数
Python3为了防止小明进一步不满意,还添加了一种新的参数类型:keyword-olny参数。
在Python3之前,可变位置参数之后就是可变关键词参数,可变关键词参数可以接受任何键值对,但我们无法对传入的键值对做出限定,函数调用者可以随意传入或不传入任何参数。
小明如果想要在可变位置参数之后在对传参行为进行限定,必须传入x和y两个关键字参数,那么他可以这么做:
>>> def func5(*args,x,y):
... return args,x,y
...
>>> func5(1,2,3,x=4,y=5)
((1, 2, 3), 4, 5)
如果不按照关键字参数进行传参,所有的位置参数都会被 '*args' 截获,后面的x和y拿不到任何实参,函数就会报错。
我们在定义函数的参数列表时,必须按照位置参数、默认参数、可变位置参数、keyword-only参数、可变关键字参数的舒徐进行定义。
想一想为什么必须是这样的顺序?
参数解构
小明已经搞懂了各种不同类型的参数,但他还有一个想法:当传入一个可迭代对象时,函数实际上是将其作为一个实参使用,想要将其内的值取出来还得自行迭代。那能不能传入一个对象,直接把它拆分开来,作为多个实参?
这当然也是可以的。给函数提供实参的时候,我们可以在集合类型前加'*'或者'**'将其解构,非字典类型使用单个星号,字典类型使用两个星号。
>>> def func6(a,b,c,d,e,f,g,h):
... return a,b,c,d,e,f,g,h
...
>>> func6(*[1,2],*(3,4),*{5,6},**{'g':7,'h':8})
(1, 2, 3, 4, 5, 6, 7, 8)
思考一下:参数解构能不能与可变参数相结合?
ok,写到这里,函数参数基本上已经讲解完毕。
这篇文章中我们回顾了函数的参数,包括位置参数、默认参数、可变位置参数、keyword-olny参数、可变关键字参数,随后又简单了解了参数结构的相关内容。
需要了解跟多内容,请参考官方文档。
下篇文章我们将回顾Python函数的作用域相关内容。