- 函数的基本定义
- 函数参数
- 返回值
- 局部变量和全局变量
- 嵌套函数
- 匿名函数
- 高阶函数
- 递归
如今你的老板让你写一个监控程序,24小时整年午无休的监控大家公司网站服务器的运行情况,当cpu\memory\disk等指标的使用量超过阈值时即发送报警邮件:java
while True: if cpu利用率 > 90%: #发送邮件提醒 链接邮箱服务器 发送邮件 关闭链接 if 硬盘使用空间 > 90%: #发送邮件提醒 链接邮箱服务器 发送邮件 关闭链接 if 内存占用 > 80%: #发送邮件提醒 链接邮箱服务器 发送邮件 关闭链接
那么当你的同事看到这个代码的时候,就发现了代码的重复性比较高,每次报警都要重写一段发邮件的代码,一个劲的copy and paste根本就不符合高端程序员的气质,其次若是之后想修改发邮件的代码,好比加上群发功能,那么就须要在全部代码上都要修改一遍.python
你也看出来了这个问题,你也不想去写重复代码,但又不知道怎么写,此时你的同时笑着和你说,这个很简单,只要把重复的代码提出出来,放在一个公共的地方,起个名字,之后谁想用这个代码,就经过这个名字就能够调用了.,以下:linux
def 发送邮件(内容): # 发送邮件提醒 链接邮件服务器 发送邮件 关闭链接 while True: if cpu利用率 > 90%: 发送邮件('CPU报警') if 硬盘使用空间 > 90%: 发送邮件('硬盘报警') if 内存占用 > 80%: 发送邮件('内存报警')
函数一词来源于数学,但编程中的「函数」概念,与数学中的函数是有很大不一样的,编程中的函数在英文中也有不少不一样的叫法。在BASIC中叫作subroutine(子过程或子程序),在Pascal中叫作procedure(过程)和function,在C中只有function,在Java里面叫作method。程序员
定义:函数是指将一组语句的集合经过一个名字(函数名)封装起来,要想执行这个函数,只需调用其函数名便可编程
- 减小重复代码
- 使程序变得可扩展
- 使程序变得可维护
def sayhi(): # sayhi函数名 print('hello world') sayhi() # 调用函数
形参变量 只有在调用时猜分配内存单元,在调用结束后,即刻释放所分配的内存单元.所以,形参只在函数内部有效.函数调用结束返回主调用函数后则不能再使用该形参变量服务器
实参 能够是常量\变量\表达式\函数等,不管实参是那种类型的量,再进行函数调用时,他们都必须有肯定的值,以便把这些值传送给形参.所以应预先用赋值,输入等方法使参数得到肯定值.运维
def cacl(x,y): # 此时的x,y是形参 res = x**y return res c = cacl(2,5) # 此时的2,5是形参 print(c)
看以下代码:函数
def stu_register(name,age,country,course): print('-----注册学生信息-----') print('姓名:',name) print('年龄:',age) print('国家:',country) print('课程:',course) stu_register('老王',22,'CHINA','PYTHON') stu_register('小明',18,'CHINA','JAVA') stu_register('康康',19,'CHINA','C++')
运行结果以下:网站
-----注册学生信息----- 姓名: 老王 年龄: 22 国家: CHINA 课程: PYTHON -----注册学生信息----- 姓名: 小明 年龄: 18 国家: CHINA 课程: JAVA -----注册学生信息----- 姓名: 康康 年龄: 19 国家: CHINA 课程: C++
这时候咱们发现了一个问题,咱们发现country这个参数的值都是CHINA,就像咱们在注册网站的时候,像国家这种信息,你不填写的话,那么默认是CHINA,这就是经过默认参数实现的,把country变成默认参数很是简单.ui
def stu_register(name,age,course,country='CHINA'):
这样,这个参数在调用时不指定,那么默认就是CHINA了,若是指定的话,就是你本身指定的值了.举个例子看看:
# 默认参数--若是用户指定了参数值,则使用用户指定的值,不然使用默认参数的值。 def stu_register(name,age,course,country='CHINA',): print('-----注册学生信息-----') print('姓名:',name) print('年龄:',age) print('国家:',country) print('课程:',course) stu_register('老王',22,'PYTHON') stu_register('小明',18,'JAVA','JAPAN') # 指定了country = JAPAN
那么咱们看一下运行结果:
-----注册学生信息----- 姓名: 老王 年龄: 22 国家: CHINA 课程: PYTHON -----注册学生信息----- 姓名: 小明 年龄: 18 国家: JAPAN 课程: JAVA
那么,为何默认参数要放置在参数的最后一个位置呢?让咱们来看下:
def stu_register(name,age,country='CHINA',course): # 若是写成这样的话,那么会当即报错,根本就不会运行,由于将实参传递给形参时,不知道CHINA是传给谁的 print('-----注册学生信息-----') print('姓名:',name) print('年龄:',age) print('国家:',country) print('课程:',course) stu_register('康康',19,'CHINA','C++')
正常状况下,给函数传参数要按顺序,不想按顺序就能够用关键参数,只需指定参数名就能够了(指定参数名的参数就叫作关键参数),注意:关键参数必须放在位置参数以后(以位置顺序肯定对应关系的参数) 举个例子看一下:
def stu_register(name,age,course='python',country='CHINA'): # 设置两个默认参数 print('-----注册学生信息-----') print('姓名:',name) print('年龄:',age) print('国家:',country) print('课程:',course) stu_register('王佳',course='LIUNX',age = 22)
可是不能够这样调用:
stu_register('小明',course='java ',22,country='JAPAN') 也不能够这样 stu_register('小明',22,23,country='JAPAN') # 这句话等于将22 23传递给了age,可是不能传递两个值
若你的函数中不肯定要传入多少参数,就可使用非固定函数,举个例子来讲,文章开头的邮件报警,如今我不想只对一我的发邮件,我想对整个运维部的人发送报警邮件,那么这个时候使用非固定参数最合适不过了:
def students(name,age,*args): # *args 会把多传入的参数变成一个元组形式 print(name,age,args) # 打印name,age,args students('小明',22,'PYTHON','CHINA') # 小明 22 ('PYTHON', 'CHINA')
原来,*args所接受的参数是用元组的形式保存的
那么如今还有一个**kwargs
def stu(name,age,*args,**kwargs): print(name,age,args,kwargs) stu('肖亚飞',22) # 肖亚飞 22 () {} # 让咱们看看args和kwargs的差异在哪? stu('肖爸爸',23,'python','linux') # 肖爸爸 23 ('python', 'linux') {} stu('肖爸爸',23,'python','linux',addr = 'Henan',music = '蝴蝶') # 肖爸爸 23 ('python', 'linux') {'addr': 'Henan', 'music': '蝴蝶'}
总结:
1.*args必须放在**kwargs以前 2.*args 没有key值,**kwargs有key值 3.使用*args和**kwargs能够很是方便的定义函数,同时能够增强扩展性,以便往后的代码维护
函数外部的代码想要获取函数的执行结果,就能够在函数里使用return语句把结果返回
def student(name,age,course='PYTHON',country='CN'): # 设置了course和country两个默认参数 print('-----注册学生信息-----') print('姓名:',name) print('年龄:',age) print('国家:',country) print('课程:',course) if age > 20: # 判断若是age大于20 return False # 返回False else: return True result = student('张三',22) # 函数外部的代码想要获取函数的执行结果,就须要使用一个变量来进行接受 if result: # 若是result = True print('恭喜注册成功!') else: # 若是result = Flase print('年纪太大!')
看一下运行结果:
-----注册学生信息----- 姓名: 张三 年龄: 22 国家: CN 课程: PYTHON 年纪太大!
注意
- 函数在执行过程当中,只要遇到return语句,就会中止执行并返回结果,so也能够理解为return语句表明着函数的结束
- 若是未在函数中指定return,那么这个函数的返回值为None
name = 'xiaoyafei' def change_name(name): # 定义一个函数,传递Name参数 print('修改前:',name) name = '肖亚飞' print('修改后:',name) change_name(name) print('在外面看修改过了吗?',name)
输出结果为:
修改前: xiaoyafei # 没修改以前的 修改后: 肖亚飞 # 在函数里修改过的 在外面看修改过了吗? xiaoyafei
不用传name参数,在函数里面仍是能够调用外面的变量
name = "xiaoyafei" def change_name(): name = '肖亚飞' print("修改后:",name) change_name() print("在外面看:",name)
运行结果为:
修改后: 肖亚飞 在外面看: xiaoyafei
那么,为啥我明明修改过了,为啥在外面仍是没修改呢?让咱们看看name变量的空间地址:
name = "xiaoyafei" def change_name(): name = '肖亚飞' print("函数内,id:",id(name)) change_name() print("函数外,id:",id(name))
结果以下:
函数内,id: 1388194004688 函数外,id: 1388193784688
要是不放大你的眼睛还真的看不清楚呢,内存空间地址不一样,因此两个name是没有关系的,那么为啥一个函数里能定义两个甚至更多相同的变量呢?
- 在函数中定义的变量称为全局变量,在程序的一开始定义的变量称为全局变量
- 全局变量的做用域是整个程序,局部变量的做用是定义该变量的函数
- 当全局变量和局部变量相同时,在定义局部变量的函数内,局部变量起做用;在其余地方全局变量起做用
做用域,程序设计概念,一般来讲,一段代码中所用到的名字并不老是有效/可用的,而限定这个名字的可用性的代码范围就是这个名字的做用与.
name = '肖亚飞' def change_name(): global name # global的做用就是在函数里声明全局变量name,意味着最上面的name = '肖亚飞'即便不写,程序最后面也能够print name name = 'xiaoyafei' print('函数里修改:',name) change_name() # 调用函数 print('函数外:',name )
运行结果以下:
函数里修改: xiaoyafei 函数外: xiaoyafei
name = 'xiaoyafei' def change_name(): name = 'xiaoyafei2' def change_name2(): name = 'xiaoyafei3' # 若是在当前函数拥有name变量,则使用函数内的name变量,若是没有,则会去上一级去找name,一直找到全局变量 print('第3层打印:',name) # 第3层打印: xiaoyafei3 change_name2() print('第2层的音:',name) # 第2层的音: xiaoyafei2 change_name() print('第一层打印:',name) # 第一层打印: xiaoyafei
匿名函数就是不须要显示的指定函数名
匿名函数的声明
res1 = lambda x,y:x+y #声明一个匿名函数,有x和y两个参数,操做为x+y print(res1) # <function <lambda> at 0x0000021D32590D08>
常规函数和匿名函数:
# 常规函数 def cacl(x,y): return x**y res = cacl(2,3) # 若是想要使用函数的返回值,就须要定义一个变量来接收 print(res) # 8 # 匿名函数 res = lambda x,y:x**y print(res(2,3)) # 8
匿名函数的优势:
- 使用python写一些脚本时,使用lambda能够省去自定义函数的过程,让代码更加精简
- 对于一些抽象的,不会被别的地方再重复使用的函数,有时候函数起个名字也是个难题,使用lambda不用考虑这些问题
- 使用lambda在某些时候让代码更容易理解
data = list(range(10)) print(data) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] # 问题:用两种方法计算这些数的平方 # 常规操做 for index,i in enumerate(data): data[i] = i*i print(data) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] # 装逼操做 res = list(map(lambda x:x*x,data)) print(res) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
那么在方法2中的map()函数是啥意思?
map()会根据提供的函数对指定序列作映射
第一个参数functrion以参数序列中的每个元素调用function函数,返回包含每次function函数返回值的新列表,能够不懂,下面就会讲,
今天不是看到map()函数,我还真不知道python2和python3中的map()有区别,让咱们举个例子看一下map()函数的用法:
在python2中: >>> def cacl(x): # 求一个数的平方 ... return x**2 ... >>> map(cacl,[1,2,3,4,5]) # 在python2中返回的是列表 [1, 4, 9, 16, 25] >>> 在python3中: >>> def cacl(x): ... return x**2 ... >>> map(cacl,[1,2,3,4,5]) # python3中返回的是迭代器对象,何谓迭代器?晚点会在博客中说明 <map object at 0x0000015B373533C8> >>> res = map(cacl,[1,2,3,4,5]) >>> next(res) # 返回迭代器中的下一个值 1 >>> next(res) 4
map()函数在python2和python3中的区别:
- 在python2中返回的是列表
- 在python3中返回的是迭代器对象
map()是python内置的高阶函数,它接收一个函数cacl和一个list,并经过函数cacl依次做用于list的每一个元素上,而且获得一个新的list并返回,因为list包含的元素能够是各类类型的,所以,map()不只仅能够处理只包含数值的list,事实上它也能够处理包含任意类型的list,只要传入的函数cacl能够处理这种数据类型.
变量能够指向函数,函数的参数能接受变量,那么一个函数就能够接收另外一个函数做为参数,这种函数就被称为高阶函数.
- 接收一个或多个函数做为输入
- return 返回另一个函数
def func2(x,y): return abs,x,y res = func2(-1,10) print(res) # (<built-in function abs>, -1, 10) def cacl(x,y,f): return f(x)+f(y) res = cacl(-4,10,abs) print(res) # 14