函数定义:
函数是指将一组语句的集合通过一个名字(函数名)封装起来,要想执行这个函数,只需调用其函数名即可
作用:
- 减少重复代码
- 使程序变的可扩展
- 使程序变的易维护
语法定义
>>> def <name>(arg1,arg2,... argN): ...
return <value>
如上所示,def的首行定义了函数名,赋值给了函数对象,并在括号中包含了0个或以上的参数(有些时候称为是形参)。在函数调用的时候,在首行的参数名赋值给了括号中的传递来的对象。
一 函数参数
1. 参数和共享引用
考虑如下代码:
>>> def f(a): a = 99 #只改变了本地变量a的值 >>> b = 88 >>> f(b) #a和b都引用了相同的值88 >>> print(b) #b的值不变,还是88 88 #自动对传入的参数进行赋值的效果等同于运行一系列简单的赋值语句,看下面 >>> b = 88 >>> a = b >>> a = 99 >>> print(b) 88
再考虑如下代码:
>>> def changer(a,b): a = 2 b[0] = 'spam' >>> X = 1 >>> L =[1,2] >>> changer(X,L) >>> X,L #X的值没有改变,L的值变了,因为L是可变类型 (1, ['spam', 2]) #等同于下面 >>> X = 1 >>> L = [1,2] >>> a = X >>> b = L >>> a = 2 >>> b[0] = 'spam' >>> X,L (1, ['spam', 2])
注:在实际应用中,应避免可变参数的修改。
可以使用以下方法避免
>>> def changer(a,b): a = 2 b = b[:] #在函数内部进行拷贝 b[0] = 'spam' >>> X = 1 >>> L = [1,2] >>> changer(X,L) >>> X,L (1, [1, 2]) 或者 >>> def changer(a,b): a = 2 b[0] = 'spam' >>> X = 1 >>> L =[1,2] >>> changer(X,L[:]) #在调用时对列表进行拷贝 >>> X,L (1, [1, 2])
这两种拷贝机制都不会阻止函数改变对象:这样做仅仅是防止了这些改变会影响调用者。为了真正意义上防止这些改变,我们总是能够将可变对象转换为不可变对象来杜绝这种问题。例如,使用元组,在试图改变时会抛出一个异常。
>>> L = [1,2] >>> changer(X,tuple(L))
2. 默认参数
看下面代码
>>> import pymysql >>> def connect_mysql(host,user,passwd,db,port): pymysql.connect(host,user,passwd,db,port) print('-----连接成功!!!-----') print('主机:',host) print('用户:',user) print('密码:',passwd) print('数据库:',db) print('端口:',port) >>> connect_mysql('192.168.189.136','ZhangSan','zhangsan123','testdb',3306) >>> connect_mysql('192.168.189.136','LiSi','lisi213','aisdb',3306) >>> connect_mysql('192.168.189.136','WangWu','wangwu321','gpsdb',3306)
在生产环境中,不同应用去连同一套mysql数据库,主机名都指向同一个数据库地址,端口号,如果不填写,默认就是3306,这就是通过默认参数实现的
>>> def connect_mysql(user,passwd,db,host='192.168.189.136',port=3306):
这样,如果主机名和端口不指定,就用默认的。指定了的话,就用你指定的值。
还要注意,默认参数不能放在普通参数之前。
3. 关键字参数
正常情况下,给函数传参数要按顺序,不想按顺序就可以用关键参数,只需指定参数名即可。也可以混用,但记住一个要求就是,关键参数不能放在位置参数之前。
>>> connect_mysql('ZhaoLiu',db='testdb',passwd='zhaoliu132')
注意:name = value的形式在调用时和def中有两种不同的含义(在调用时代表关键字参数,而在函数头部代表默认值参数)
4. 非固定参数
如果你的函数在定义时不确定用户想传入多少个参数,就可以使用非固定参数
>>> def stu_register(name,age,*args): # *args 会把多传入的位置参数变成一个元组形式 print(name,age,args) >>> stu_register('ZhangSan',22) ZhangSan 22 () #后面这个()就是args,只是因为没传值,所以为空 >>> stu_register('Tom',27,'CN','Python') Tom 27 ('CN', 'Python')
还可以有一个**kwargs
>>> def stu_register(name,age,*args,**kwargs): # *kwargs 会把多传入的关键字参数变成一个dict形式 print(name,age,args,kwargs) >>> stu_register('ZhangSan',22) ZhangSan 22 () {} #后面这个{}就是kargs,只是因为没传值,所以为空 >>> stu_register('Tom',27,'CN','Python',sex='Male',interest='swim') Tom 27 ('CN', 'Python') {'sex': 'Male', 'interest': 'swim'}
注意!**kwargs参数必须放在最后
!!!在Python3.0中还有keyword-only参数(最好不要用,容易留坑)
>>> def stu_register(name,*args,age,**kwargs): #age参数放在*args参数后面,此时,age就是keyword-only参数 print(name,args,age,kwargs) >>> stu_register('ZhangSan','Male',age=33,course='Python') #传递age只能用关键字参数 ZhangSan ('Male',) 33 {'course': 'Python', 'interest': 'swim'} >>> def stu_register(name,*args,age=28,**kwargs): #带有默认值的keyword-only参数在调用时都是可选的 print(name,args,age,kwargs) >>> stu_register('ZhangSan','Male',course='Python',interest='swim') #此时,没有传递age参数,那么就用默认值 ZhangSan ('Male',) 28 {'course': 'Python', 'interest': 'swim'} >>> def stu_register(name,*,age,**kwargs): print(name,age,kwargs) >>> stu_register('ZhangSan','Male',age=33,course='Python',interest='swim') #在*前面只能传对应的参数个数 Traceback (most recent call last): File "<pyshell#70>", line 1, in <module> stu_register('ZhangSan','Male',age=33,course='Python',interest='swim') TypeError: stu_register() takes 1 positional argument but 2 positional arguments (and 1 keyword-only argument) were given >>> stu_register('ZhangSan',age=33,course='Python',interest='swim') ZhangSan 33 {'course': 'Python', 'interest': 'swim'}
二 返回值
函数是一个功能块,该功能到底执行成功与否,需要通过返回值来告知调用者。
def 备份数据库(): 备份数据库的代码... if 备份成功: return True else: return False STATUS = 备份数据库() #每次执行备份函数,会将执行结果返回给STATUS if not STATUS: #如果备份失败,就发邮件告警 记录日志,发送邮件代码...
注意:
- 函数在执行过程中只要遇到return语句,就会停止执行并返回结果,so 也可以理解为 return 语句代表着函数的结束
- 如果未在函数中指定return,那这个函数的返回值为None
- 函数可以返回任意数据类型
三 递归函数
在函数内部,可以调用其它函数。如果一个函数在内部调用自身本身,这个函数就是递归函数。
>>> def calc(n): print(n) if int(n/2) == 0: return n return calc(int(n/2)) >>> calc(10) 10 5 2 1 1
递归特性:
- 必须有一个明确的结束条件
- 每次进入更深一层递归时,问题规模相比上次递归都应有所减少
- 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出)
四 高阶函数
变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。
>>> def add(x,y,f): return f(x) + f(y) >>> res = add(3,-6,abs) >>> print(res) 9