函数
一 数学定义的函数与python中的函数
初中数学函数定义:一般的,在一个变化过程中,如果有两个变量x和y,并且对于x的每一个确定的值,y都有唯一确定的值与其对应,那么我们就把x称为自变量,把y称为因变量,y是x的函数。自变量x的取值范围叫做这个函数的定义域
例如y=2*x
python中函数定义:函数是逻辑结构化和过程化的一种编程方法。
1 python中函数定义方法: 2 3 def test(x): 4 "The function definitions" 5 x+=1 6 return x 7 8 def:定义函数的关键字 9 test:函数名 10 ():内可定义形参 11 "":文档描述(非必要,但是强烈建议为你的函数添加描述信息) 12 x+=1:泛指代码块或程序处理逻辑 13 return:定义返回值
调用运行:可以带参数也可以不带
函数名()
补充:
1.编程语言中的函数与数学意义的函数是截然不同的俩个概念,编程语言中的函数是通过一个函数名封装好一串用来完成某一特定功能的逻辑,数学定义的函数就是一个等式,等式在传入因变量值x不同会得到一个结果y,这一点与编程语言中类似(也是传入一个参数,得到一个返回值),不同的是数学意义的函数,传入值相同,得到的结果必然相同且没有任何变量的修改(不修改状态),而编程语言中的函数传入的参数相同返回值可不一定相同且可以修改其他的全局变量值(因为一个函数a的执行可能依赖于另外一个函数b的结果,b可能得到不同结果,那即便是你给a传入相同的参数,那么a得到的结果也肯定不同)
2.函数式编程就是:先定义一个数学函数(数学建模),然后按照这个数学模型用编程语言去实现它。至于具体如何实现和这么做的好处,且看后续的函数式编程。
二 为何使用函数
背景提要
现在老板让你写一个监控程序,监控服务器的系统状况,当cpu\memory\disk等指标的使用量超过阀值时即发邮件报警,你掏空了所有的知识量,写出了以下代码
1 while True: 2 if cpu利用率 > 90%: 3 #发送邮件提醒 4 连接邮箱服务器 5 发送邮件 6 关闭连接 7 8 if 硬盘使用空间 > 90%: 9 #发送邮件提醒 10 连接邮箱服务器 11 发送邮件 12 关闭连接 13 14 if 内存占用 > 80%: 15 #发送邮件提醒 16 连接邮箱服务器 17 发送邮件 18 关闭连接
- 代码重复过多,一个劲的copy and paste不符合高端程序员的气质
- 如果日后需要修改发邮件的这段代码,比如加入群发功能,那你就需要在所有用到这段代码的地方都修改一遍
def 发送邮件(内容) #发送邮件提醒 连接邮箱服务器 发送邮件 关闭连接 while True: if cpu利用率 > 90%: 发送邮件('CPU报警') if 硬盘使用空间 > 90%: 发送邮件('硬盘报警') if 内存占用 > 80%: 发送邮件('内存报警')
总结使用函数的好处:
1.代码重用
2.保持一致性,易维护
3.可扩展性
三 函数和过程
过程定义:过程就是简单特殊没有返回值的函数
这么看来我们在讨论为何使用函数的的时候引入的函数,都没有返回值,没有返回值就是过程,没错,但是在python中有比较神奇的事情
1 def test01(): 2 msg='hello The little green frog' 3 print msg 4 5 def test02(): 6 msg='hello WuDaLang' 7 print msg 8 return msg 9 10 11 t1=test01() 12 13 t2=test02() 14 15 16 print 'from test01 return is [%s]' %t1 17 print 'from test02 return is [%s]' %t2
总结:当一个函数/过程没有使用return显示的定义返回值时,python解释器会隐式的返回None,
所以在python中即便是过程也可以算作函数。
1 def test01(): 2 pass 3 4 def test02(): 5 return 0 6 7 def test03(): 8 return 0,10,'hello',['gou','lb'],{'WuDaLang':'lb'} 9 10 t1=test01() 11 t2=test02() 12 t3=test03() 13 14 15 print 'from test01 return is [%s]: ' %type(t1),t1 16 print 'from test02 return is [%s]: ' %type(t2),t2 17 print 'from test03 return is [%s]: ' %type(t3),t3
总结:
返回值数=0:返回None
返回值数=1:返回object
返回值数>1:返回tuple
四 函数参数
1.形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只在函数内部有效。函数调用结束返回主调用函数后则不能再使用该形参变量
2.实参可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。因此应预先用赋值,输入等办法使参数获得确定值
3.位置参数和关键字(标准调用:实参与形参位置一一对应;关键字调用:位置无需固定)
位置参数:与形参一一对应,缺一不可,多一也不行
关键词:无需一一对应,缺一不可,多一也不
4.默认参数
def test(1,2,type='xxx')
jkk
kkk
5.参数组
** 字典 * 列表
# def test1(x,*args,**kwargs):
# print(x)
# print(args)
# print(kwargs)
# test1(1,2,3,44,y=1)
五 全局变量与局部变量
# 全局变量与局部变量: # name = 'glh' # 全局变量 # def change_name(): # global name # 意思为 下边定义的name 为全局变量 # name = '帅的一比' # 变量名相同,先从内部找,再一级一级往上找 # print('change_name',name) # change_name() # print(name) # 如果函数中无global关键字: # 一、有声明局部变量: # name = [1,2,3,4] # def change_name(): # name = '123' # print(name) #change name 结果: '123' # 二、无声明局部变量: # name = [1,2,3,4] # def change_name(): # name.append(5) # print(name) #change name 结果:[1,2,3,4,5] # 则优先读取局部变量,若无定义局部变量,则能读取全局变量,且无法给全局变量赋值,但是对应可变对象(列表,字典)可以对内部元素操作(增加,删除) # 如果函数中有global关键字: # 一、有声明局部变量: # name = [1,2,3,4] # def change_name(): # global name # name = '123' # print(name) #change name 结果: '123' ### 错误示例 ### # name = [1,2,3,4] # def change_name(): # name = '123' # global name global 必须放在局部变量前边 # print(name) #change name 结果: 报错 # 二、无声明局部变量: # name = [1,2,3,4] # def change_name(): # global name # name = '56' # 重新 定义全局变量 # print(name) #change name 结果:'56' # 如果函数中有global关键字,变量本质上就说全局变量那个值,可读取,可赋值 # name = 'glh' # def change_name(): # global name # 下边的 name 为 全局变量 # name = 'zq' # print('change_name',name) # def change_name1(): # name = 'wc' # print('change_name1',name) # change_name1() # 先执行 则name = ‘wc’ # change_name() # 后执行 则name为全局(global) = ‘zq' # 规范: 全局变量 使用 大写字母 局部变量 使用 小写字母 ###### 函数嵌套 ######### # name='海风' # def huangwei(): # name = "黄伟" # print(name) # def liuyang(): # name = "刘洋" # print(name) # def nulige(): # name = '炉指花' # print(name) # print(name) # nulige() 不能在外面调用内部函数 # liuyang() # print(name) # # print(name) # huangwei() # print(name) #######嵌套中,global的用法 # name = '苟陇辉' step1 # def weibo(): # name = '沉着' step4 # def weiweibo(): # global name step6 只改变最外面的全局变量 name = ‘苟陇辉’ # name = '冷静' step7 # weiweibo() step5 # print(name) step8 # print(name) step2 # weibo() step3 # print(name) step9 # 结果: 苟陇辉 沉着 冷静 # name = '苟陇辉' # step1 # def weibo(): # name = '沉着' # step4 # def weiweibo(): # nonlocal name # step6 指定上一级变量 name = '沉着' # name = '冷静' # step7 # weiweibo() #step5 # print(name) # step8 # print(name) # step2 # weibo() # step3 # print(name) # step9
1 定义: 2 名称空间:当程序运行时,逐行读取,遇到变量与值,它会在内存中开辟一块内存空间, 3 来存放变量与值得内存地址的对应关系,这样的用来储存变量与值得内存对应关系的空间叫做名称空间. 4 内置名称空间 5 全局名称空间 6 局部名称空间 7 加载顺序: 内置名称空间--->全局名称空间--->局部名称空间 8 读取顺序: 局部名称空间--->全局名称空间--->内置名称空间 9 作用域: 10 全局作用域:内置名称空间 全局名称空间 11 局部作用域:局部名称空间
六 前向引用
风湿理论
# 风湿理论 # 函数即变量 # 调用前,先定义, name = 'glh' def foo(): # 将 foo 函数 的代码块 存放在内存中 pass def bar(): # 将 bar 函数 的代码块 存放在内存中 pass foo()
注意: 从上往下一步步执行,遇到def 把其中的代码 当做一个整体 跳一步,调用def时,再具体执行
七 递归调用
在函数内部,可以调用其他函数。
def calc(n): print(n) if int(n/2) == 0: return n res = calc(int(n/2)) return res calc(10)
print(res)

import time person_list = ['老管头', '蛋蛋', '太章', '苟陇辉'] def ask_way(argv): print('*'*60) if argv == []: return '没人知道' person = argv.pop(0) if person == '苟陇辉': return '%s 说我知道,长安大学在未央区朱宏路北段,嘻嘻' % person print('%s 你好,你知道长安大学怎么走吗?' % person) time.sleep(1) print('%s 说我不知道,你去问问 %s' % (person, argv[0])) time.sleep(3) return ask_way(argv) print(ask_way(person_list))
递归特性:
1. 必须有一个明确的结束条件
2. 每次进入更深一层递归时,问题规模相比上次递归都应有所减少
3. 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出)
八 作用域
作用域在定义函数时就已经固定住了,不会随着调用位置的改变而改变
# name = 'gou' # def foo(): # name = 'goulonghui' # def bar(): # print(name) # return bar # res = foo() # print(res) # 打印出的 是函数名为bar的内存地址 # name = 'gou' # def foo(): # name = 'goulonghui' # def bar(): # print(name) # return bar # res = foo() # # print(res) # res() # 运行res res即为函数bar的内存地址 ,运行res 即为运行res name = 'gou' def foo(): name = 'goulognhui' def bar(): name = 'zhangqian' def tt(): print(name) return tt return bar # bar = foo() # # print(bar) 打印bar的内存地址 # tt = bar() # # print(tt) 打印tt的内存地址 # tt() # res = foo()()() 和上面一步一步运行一样
九 匿名函数
匿名函数就是不需要显式的指定函数。
# lambda x:x+1 匿名函数的 函数体 # 等同于 # def calc(x): # return x+1 # print(lambda x:x+1) 获得lambda的内存地址 <function <lambda> at 0x00000000001F3E18> # func = lambda x:x+1 # print(func(5)) # 实现一个功能,把用户输入的名字后面加上_sb # name = input(">>>") # func = lambda x:x + '_sb' # func = lambda name:name + '_sb' # print(func(name)) # func = lambda x,y,z:(x+1,y+1,y+z) # print(func(1,2,3,)) #作用:与其他函数连用,比如:
res = map(lambda x:x**2,[1,5,7,4,8])
for i in res:
print(i)
输出
1
25
49
16
64
十 函数式编程
# 高阶函数 # 满足条件一:把函数当做参数传给另外一个函数 # def foo(n): # print(n) # def bar(name): # print(name) # foo(bar('goulonghui')) ### 结果 ### goulonghui None # 满足条件二:函数返回值中包含函数 # def bar(): # print('gou') # def foo(n): # print(n) # return bar # 调用别的函数 # foo(1)() ### 结果 ### # 1 # gou # def hanle(): # print('from hanle') # return hanle # 调用自身 # n = hanle() # print(n) # 尾调用 # 在函数的最后一步调用另外一个函数。(最后一行不一定是函数的最后一步) # def calc(n): # if n > 1: # return n # 此处为函数的最后一步,但是不是最后一行 # elif n == 1: # return n+1 # 此处为函数的最后一步,但是不是最后一行 # else: # pass # print(calc(1)) # 尾调用优化: 由于是函数的最后一步操作,所以不需要保留外层函数的调用记录,因为调动位置、内部变量等信息都不会 # 在用到了,只要直接用内层函数的调用记录,取代外层函数的调用记录就可以了。
三个函数:
map 函数
# 实现一个功能,将下面列表中的数字每个都加1 # array = [1,2,10,5,7] # for 循环实现 # ret = [] # for i in array: # ret.append(i+1) ## ret.append(i**2)平方 # print(ret) # 如果我们有一万个列表,那么你只能把上面的逻辑定义成函数 # def map_test(array): # ret = [] # for i in array: # ret.append(i+1) ## ret.append(i**2)平方 # return ret # print(map_test(array)) #如果我们的需求变了,不是把列表中每个元素都加1,还有平方,减一,那么可以这样 # array = [1,2,10,5,7] # def add_num(x): # return x**2 # def reduce_num(x): # return x-1 # def map_test(func,array): # ret = [] # for i in array: # ret.append(func(i)) ## ret.append(i**2)平方 # return ret # print(map_test(reduce_num,array)) # print(map_test(lambda x:x-1,array)) # print(map_test(add_num,array)) # print(map_test(lambda x:x**2,array)) # 上述即为map函数的功能,map得到的结果为可迭代对象 # print(map(lambda x:x+1,range(5))) ### <map object at 0x0000000002839208> ### 得到一个地址(可迭代对象的地址) # res = map(lambda x:x+1,range(5)) # print(res) # for i in res: # print(i) # print(list(res)) # 得到一个空列表 因为map函数得到的结果为迭代器 只能迭代一次 # res = map(lambda x:x+1,range(5)) # # print(res) # # for i in res: # # print(i) # print(list(res)) ### [1, 2, 3, 4, 5] ### # array = [1,2,10,5,7] # def reduce_num(x): # return x-1 # print(list(map(reduce_num,array))) # 其中reduce_num 为可迭代对象,array 也为可迭代对象 # # msg = 'goulonghui' # print(list(map(lambda x:x.upper(),msg))) ### ['G', 'O', 'U', 'L', 'O', 'N', 'G', 'H', 'U', 'I'] ### # 在python2 中map函数得到的结果就是一个列表
# filter 函数 # moive_people = ['gou_sb','goulonghui','hasu_sb'] # def sb_show(n): # return n.endswith('sb') # # 等于 lambda n:n.endswith('sb') # def filter_test(func,array): # ret = [] # for n in array: # if not func(n): # ret.append(n) # return ret # print(list(filter_test(sb_show,moive_people))) ### ['goulonghui'] ### # print(list(filter(lambda n:not n.endswith('sb'),moive_people))) ### ['goulonghui'] ### # print(filter(lambda n:not n.endswith('sb'),moive_people)) ### <filter object at 0x0000000001E79518> ### # filter 函数 后边也为可迭代对象,当前面定义的函数结果为True,则保留结果 # reduce函数 # num1 = [1,2,3,100] # res = 1 # for i in num1: # res=i*res # print(res) # def reduce_test(array): # res = 1 # for i in array: # res = i * res # return res # print(reduce_test(num1)) # num1 = [1,2,3,100] # def mutil(x,y): # return x*y # # 等于 lambda x,y:x*y # def reduce_test(func,array): # res = array.pop(0) # 先把第一个值拿出来 # for i in array: # res = func(res,i) # return res # print(reduce_test(mutil,num1)) # print(reduce_test(lambda x,y:x*y,num1)) # num1 = [1,2,3,100] # from functools import reduce # print(reduce(lambda x,y:x+y,num1,100)) # 100位指定一个初始值init ### 206 ### # print(reduce(lambda x,y:x+y,num1,)) ### 106 ### #总结# # map函数 # 处理序列中的每一个元素,得到的结果是一个'列表',该'列表'元素个数、位置与原来一样。 # filter 函数 # 遍历序列中的每一个元素,判断每个元素,得到一个bool值,若结果为Ture,则留下来。 people = [ {'name':'goulonghui','age':18}, {'name':'gou','age':1000}, {'name':'zhangqian','age':10000} ] print(filter(lambda p:p['age'] <= 18,people)) ### <filter object at 0x00000000021F9BE0> ### print(list(filter(lambda p:p['age'] <= 18,people))) ### [{'name': 'goulonghui', 'age': 18}] ### #reduce函数 # 处理一个序列,然后把序列进行合并操作。 #用来计算1到100的和 from functools import reduce print(reduce(lambda x,y:x+y,range(100),100)) print(reduce(lambda x,y:x+y,range(1,101)))