大多数时候,我们调用函数时,需要传入指定的参数,根据我们传入的参数,函数将返回我们对应参数的结果。在Python定义参数比较简单,灵活度特别大。除了正常定义的必选参数外,还有默认参数、可变参数、关键字参数,使函数定义的接口,不但能处理复杂的参数,还能简化调用者的代码。
位置参数:
>>> def power(x):
... return x*x
上述写的是个简单的求平方的示例,其中 x 就是位置参数。我们调用时只能传入一个参数,会报错;调用时传入一个非数字类型的参数,也会报错。下面会逐渐完善这个函数。
若我们想求一个数的3次方或4次方,写多个函数会很麻烦,现有函数函数加一个参数即可解决这个问题,如下所示:
>>> def power(x,n):
... sum = 1
... while n>0:
... sum = sum * x
... n = n - 1
... return sum
上述代码中 x 和 n 都是位置参数,调用函数时,需按照顺序放值。
新的函数写好后,测试没有问题,但输入一个参数的时候会报错。为了解决这一问题,我们需要设置默认参数。
默认参数:
>>> def power(x,n=2):
... sum = 1
... while n>0:
... sum = sum * x
... n = n - 1
... return sum
上述代码参数中,加入了 n=2 ,调用时如果不输入第二个参数时,函数默认输入的是2,若输入其他数字,则根据用户输入的为准。使用默认参数,有一点需要注意下:
变化大的函数放在前面,变化小的函数放在后面,变化小的函数可以写为默认函数(使用时,默认函数尽量放在后面,而且不要放在首位。默认参数放在首位的话,还有其他需要输入的参数,系统无法确认用户第一个输入的参数是修改默认参数还是默认参数后的其他参数,编译器会报错)。
下面举一个比较复杂的例子。比如,你要录入一批会员信息,会员主要信息包含:姓名、性别、年龄、城市等。由于大部分会员都是男性,且都在一个城市,则函数可以这样写:
>>> def hm(name,age,sex='boy',city='Harbin'):
... return name+','+age+','+sex+','+city
上述函数,只有 name 和 age 参数时必填项,其他两个是选填,若 输入前两个参数,示例如下:
>>> hm('Susan','20')
结果如下:
'Susan,20,boy,Harbin'
若性别是女孩,则写法如下:
>>> hm('Susan','20','gril')
结果如下:
'Susan,20,gril,Harbin'
若性别不变,只改变城市,写三个参数即可,写法如下:
>>> hm('Susan','20',city='Beijing')
结果如下:
'Susan,20,boy,Beijing'
综上所述,默认函数可以简化很多步骤,降低了难度,需要复杂调用时,传入更多参数即可,一件事只定义一个函数。
在默认函数这里,有个很大的坑,代码如下:
>>> def k1(L=[]):
... L.append('End')
... return L
上述代码中,给L定义了一个空的list,L变成了一个存储在内存中的变量,若正常传参调用没问题,但要是重复不传参调用,则会不断在list中添加指定字符串,示例如下:
>>> k1([1,2,3])
[1,2,3,'End']
>>> k1([1,2,3])
[1,2,3,'End']
>>> k1()
['End']
>>> k1()
['End','End']
>>> k1()
['End','End','End']
若解决这个问题,需要使用不变变量 None 来解决,示例如下:
>>> def k1(L=None):
... if L is None:
... L=[]
... L.append('End')
... return L
不变对象一旦创建,对象内部的数据就不能修改,这样就减少了由于修改数据导致的错误。此外,由于对象不变,多任务环境下同时读取对象不需要加锁,同时读一点问题都没有。我们在编写程序时,如果可以设计一个不变对象,那就尽量设计成不变对象。
可变参数:
可变参数,即传入参数的数量是可变的。一般来说,首先想到的是,传入 list 或 tuple,这样是可以的,但有一个缺陷,传入参数前需要先生成一个 list 或 tuple 再传值,这样比较麻烦,可变参数解决了这一问题,示例为数字求和,代码如下:
>>> def s1(*nums):
... sum = 0
... for n in nums:
... sum = sum + n
... return sum
若要将 list 或 tuple 中的个别值,传入函数,写法如下:
>>> ns = [1,2,3,4]
>>> s1(ns[0],ns[3])
5
>>> s1()
0
上述代码中,若不传参数,则返回函数中定义的 sum 的值,0 。
若要将整个的 list 或 tuple 中的值传进去,则写法如下:
>>> nts = (1,2,3)
>>> s1(*nts)
6
*nts 表示将 nts 中所有的值传进去,在Python中,这种写法很常见。
关键字参数:
关键字参数是将你传入的值单独装在一个tuple,示例如下:
>>> def g1(name,age,**mes):
... print('name:', name, 'age:', age, 'message:', mes)
测试结果如下:
>>> g1('Susan',20,sex='boy',city='Harbin')
name: Susan age: 20 message: {'sex': 'boy', 'city': 'Harbin'}
关键字函数拓展了函数的功能。比如我们录制一个人的信息,处了几项必填选项外,选填选项如果用户填写了,就存储在dict中,有多少存多少,满足需求。
关键字参数的传入方法和可变参数类似,从dict中取几个或全取,写法如下:
>>> gs = {'sex':'boy','city':'Harbin','tel':'1234567'}
>>> g1('Susan',20,city=gs['city'],sex=gs['sex'])
name: Susan age: 20 message: {'city':'Harbin','sex':'boy'}
>>>
>>> g1('Susan',20,**gs)
name:Susan age:20 message:{'city':Harbin,'sex':'boy','tel':'1234567'}
有一点需要注意下,传入函数的 dict ,属于拷贝数据,对其的修改不会影响到原先的 dict 。
命名关键字参数:
通过指定的参数变量名来调用函数,传值时输入指定变量名+变量值调用函数,代码如下:
>>> def m1(name,age,*,city,sex):
... print('name:',name,'age:',age,'city:',city,'sex:',sex)
命名关键字函数有一个特殊的分割符 * ,* 后面的为命名关键字参数。传参时,须写参数名,否则报错,示例如下:
>>> m1('Susan',20,city='Harbin',sex='boy')
name: Susan age: 20 city: Harbin sex: sex
错误示例如下:
>>> m1('Susan',20,'Harbin','boy')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: m1() takes 2 positional arguments but 4 were given
若函数的参数中有可变函数,则就可以不用写 命名关键字函数的分隔符 *,示例如下:
>>> def m2(name,*age,city,sex):
... print('name:',name,'age:',age,'city:',city,'sex:',sex)
结果如下:
>>> m2('Susan',[20,21],city='Harbin',sex='boy')
name: Susan age: ([20,21],) city: Harbin sex: boy
命名关键字函数的参数可以设置默认值。使用命名关键字函数时,若没有可变参数,在命名关键字参数前必须加分隔符 * ,否则视为 位置函数。
用 Python2. 7 版本 使用此方法报错。
参数组合:
在Python中,上述参数可以混合使用,但要注意的是,混合使用的参数顺序必须是:必选参数,默认参数,可变参数,命名关键字参数,关键字参数。示例如下:
>>> def h1(a,b=0,*c,d,**f):
... print('a:',a,'b:',b,'c:',c,'d:',d,'f:',f)
测试如下:
>>> h1(1,2,3,d=4,f=5)
a: 1 b: 2 c: (3,) d: 4 f: {'f': 5}
>>> h1(1,2,d=4)
a: 1 b: 2 c: () d: 4 f: {}
最神奇的一点,通过 tuple 和 dict 也可以调用上述函数,示例如下:
>>> c1 =(1,2,3,4)
>>> d1 = {'d':100,'f':'fff'}
>>> h1(*c1,**d1)
a: 1 b: 2 c: (3, 4) d: 100 f: {'f': 'fff'}
递归函数:
递归函数就是函数调用自身函数,示例如下:
>>> def sumNum(x):
... if x == 1:
... return 1
... return x + sumNum(x-1)
上述代码是求数字 1 到整数 x 之间的和,代码的末尾调用了自身函数。
递归函数的有点是定义简单,构思清晰,理论上所有的递归函数都可以写成循环的形式,但循环的逻辑不如递归的逻辑清晰。
使用递归函数时需要防止栈的溢出,栈的大小不是无限的,所以,当递归的次数太多,会导致栈溢出,编译器报错,示例如下:
>>> sumNum(1000)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in fact
...
File "<stdin>", line 4, in fact
RuntimeError: maximum recursion depth exceeded in comparison
正常来讲,解决的方法是 尾递归 优化,简单来说,调用自身函数时,语句中没有表达式,示例如下:
>>> def sumNum(x,zj):
... if x == 1:
... return zj
... return sumNum(x-1,x+zj)
但是,大多数编程语言都没有针对 尾递归 做优化,Python也不例外。所以,使用递归函数时要注意,不要过深的调用。
函数参数很重要,基础知识要打牢,本篇就到这里,继续学习~~