Python基础知识06-函数,递归,内置函数
一、为何使用函数
背景提要:在学习函数之前,一直遵循:面向过程编程,即:根据业务逻辑从上到下实现功能,其往往用一长段代码来实现指定功能,开发过程中最常见的操作就是粘贴复制,也就是将之前实现的代码块复制到现需功能处,如下:
while True: if cpu利用率 > 90%: #发送邮件提醒 连接邮箱服务器 发送邮件 关闭连接 if 硬盘使用空间 > 90%: #发送邮件提醒 连接邮箱服务器 发送邮件 关闭连接 if 内存占用 > 80%: #发送邮件提醒 连接邮箱服务器 发送邮件 关闭连接
改进:把重复的代码提取出来,放在一个公共的地方,起个名字,以后谁想用这段代码,就通过这个名字调用就行了,如下:
def 发送邮件(内容) #发送邮件提醒 连接邮箱服务器 发送邮件 关闭连接 while True: if cpu利用率 > 90%: 发送邮件('CPU报警') if 硬盘使用空间 > 90%: 发送邮件('硬盘报警') if 内存占用 > 80%: 发送邮件('内存报警')
对于上述的两种实现方式,第二次必然比第一次的重用性和可读性要好,其实这就是函数式编程和面向过程编程的区别:
- 函数式:将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可
- 面向对象:对函数进行分类和封装,让开发“更快更好更强...”
二、定义函数的方法
函数是逻辑结构化和过程化的一种编程方法;函数式编程最重要的是增强代码的重用性和可读性。
#python中函数定义方法: def test(x): "The function definitions" x +=1 return x test(3) #def:定义函数的关键字 #test:函数名 #():内可定义形参 #"":文档描述(非必要,但是强烈建议为你的函数添加描述信息) #x+=1:函数体(泛指代码块或程序处理逻辑) #return:函数体(定义返回值) #调用运行:可以带参数也可以不带 #函数名()
1、返回值
函数是一个功能块,该功能到底执行成功与否,需要通过返回值来告知调用者,没有返回值的函数也称为过程。
#!/usr/bin/env python #_*_ coding:utf-8 _*_ def test01(): msg='hello The little green frog' print msg def test02(): msg='hello WuDaLang' print msg return msg t1=test01() t2=test02() print 'from test01 return is [%s]' %t1 print 'from test02 return is [%s]' %t2 #结果 ''' hello The little green frog hello WuDaLang from test01 return is [None] from test02 return is [hello WuDaLang] '''
当一个函数/过程没有使用return显示的定义返回值时,python解释器会隐式的返回None。
2、参数
函数的有三中不同的参数:
1、普通参数:
# ######### 定义函数 ######### # name 叫做函数func的形式参数,简称:形参 def func(name): print name # ######### 执行函数 ######### # 'wupeiqi' 叫做函数func的实际参数,简称:实参 func('zhangsan') ''' 1、形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只在函数内部有效。函数调用结束返回主调用函数后则不能再使用该形参变量 2、实参可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。因此应预先用赋值,输入等办法使参数获得确定值 3、位置参数和关键字(标准调用:实参与形参位置一一对应;关键字调用:位置无需固定) '''
2、默认参数:
def func(name, age=18): print "%s:%s" % (name, age) func('zahngsan', 19) # 指定参数 func('lisi') # 使用默认参数 #注:默认参数需要放在参数列表最后
3、动态参数:传列表、字典
#动态参数一 def func(*args): #将参数包装成列表传入函数 print args func(11,33,4,4454,5) # 执行方式一 li = [11,2,2,3,3,4,54] # 执行方式二 func(*li) #结果 ''' (11, 33, 4, 4454, 5) (11, 2, 2, 3, 3, 4, 54) ''' #动态参数二 def func(**kargs): #将参数包装成字典传入函数 print kargs func(name = 'zhangsan', age = 18) # 执行方式一 li = {'name':'zhangsan', 'age':18, 'gender':'male'} # 执行方式二,字典必须先定义好 func(**li) #结果 ''' {'age': 18, 'name': 'zhangsan'} {'gender': 'male', 'age': 18, 'name': 'zhangsan'} ''' #动态参数三 def func(*args, **kwargs): print args print kwargs
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import smtplib from email.mime.text import MIMEText from email.utils import formataddr msg = MIMEText('邮件内容', 'plain', 'utf-8') msg['From'] = formataddr(["武沛齐",'wptawy@126.com']) msg['To'] = formataddr(["走人",'424662508@qq.com']) msg['Subject'] = "主题" server = smtplib.SMTP("smtp.126.com", 25) server.login("wptawy@126.com", "邮箱密码") server.sendmail('wptawy@126.com', ['424662508@qq.com',], msg.as_string()) server.quit()
三、函数是第一类对象
在python中所有的名字都没有储值功能,函数是第一个对象。
def foo(): print("yyp") foo() # 输出结果: yyp
可以当做参数
def foo(): print "tom" def func(msg): print msg msg() func(foo) #输出结果: <function foo at 0x00000000020D3E18> tom
可以当做返回值
def foo(): print "tom" def func(msg): return msg f=func(foo) print f f() #输出结果: # <function foo at 0x0000000002423E18> # tom
可以当做容器类型的一个元素
def foo(): return "tom" func_dic={'foo':foo()} print func_dic.get('foo') #输出结果: #tom
一类对象指的是:函数可以被当做数据来处理被引用
四、局部变量和全局变量
在子程序中定义的变量称为局部变量,在程序的一开始定义的变量称为全局变量。
- 全局变量作用域是整个程序,局部变量作用域是定义该变量的子程序。
- 当全局变量与局部变量同名时:在定义局部变量的子程序内,局部变量起作用;在其它地方全局变量起作用。
name='lhf' #全局变量 def change_name(): print('我的名字',name) change_name() def change_name(): name='帅了一笔' #局部变量 print('我的名字',name) change_name() print(name) def change_name(): global name name='帅了一笔' print('我的名字',name) change_name() print(name)
五、前向引用之-函数即变量
def action(): print 'in the action' logger() action() #报错NameError: global name 'logger' is not defined def logger(): print 'in the logger' def action(): print 'in the action' logger() action()
六、嵌套函数和作用域
name = "yyp" def change_name(): name = "yyp2" def change_name2(): name = "yyp3" print("第3层打印",name) change_name2() #调用内层函数 print("第2层打印",name) change_name() print("最外层打印",name)
作用域在定义函数时就已经固定住了,不会随着调用位置的改变而改变,此时,在最外层调用change_name2()会出错。
#例一: name='yyp' def foo(): name='sy' def bar(): print(name) return bar func=foo() func() 例二: name='yyp' def foo(): name='sy' def bar(): name='tom' def tt(): print(name) return tt return bar func=foo() func()()
七、递归调用
递归特性:
- 必须有一个明确的结束条件
- 每次进入更深一层递归时,问题规模相比上次递归都应有所减少
- 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出)
链接:堆栈扫盲
def calc(n): print(n) if int(n/2) ==0: return n return calc(int(n/2)) calc(10) #输出: # 10 # 5 # 2
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python #_*_ coding:utf-8 _*_ import time person_list=['alex','wupeiqi','yuanhao','linhaifeng'] def ask_way(person_list): print('-'*60) if len(person_list) == 0: return '没人知道' person=person_list.pop(0) if person == 'linhaifeng': return '%s说:我知道,老男孩就在沙河汇德商厦,下地铁就是' %person print('hi 美男[%s],敢问路在何方' %person) print('%s回答道:我不知道,但念你慧眼识猪,你等着,我帮你问问%s...' %(person,person_list)) time.sleep(3) res=ask_way(person_list) # print('%s问的结果是: %res' %(person,res)) return res res=ask_way(person_list) print(res)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
data = [1, 3, 6, 7, 9, 12, 14, 16, 17, 18, 20, 21, 22, 23, 30, 32, 33, 35] def binary_search(dataset, find_num): print(dataset) if len(dataset) > 1: mid = int(len(dataset) / 2) if dataset[mid] == find_num: # find it print("找到数字", dataset[mid]) elif dataset[mid] > find_num: # 找的数在mid左面 print("\033[31;1m找的数在mid[%s]左面\033[0m" % dataset[mid]) return binary_search(dataset[0:mid], find_num) else: # 找的数在mid右面 print("\033[32;1m找的数在mid[%s]右面\033[0m" % dataset[mid]) return binary_search(dataset[mid + 1:], find_num) else: if dataset[0] == find_num: # find it print("找到数字啦", dataset[0]) else: print("没的分了,要找的数字[%s]不在列表里" % find_num) binary_search(data, 66)
八、冒泡算法
需求:请按照从小到大对列表 [13, 22, 6, 99, 11] 进行排序
思路:相邻两个值进行比较,将较大的值放在右侧,依次比较!
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#第一步 li = [13, 22, 6, 99, 11] for m in range(4): # 等价于 #for m in range(len(li)-1): if li[m]> li[m+1]: temp = li[m+1] li[m+1] = li[m] li[m] = temp #第二步 li = [13, 22, 6, 99, 11] for m in range(4): # 等价于 #for m in range(len(li)-1): if li[m]> li[m+1]: temp = li[m+1] li[m+1] = li[m] li[m] = temp for m in range(3): # 等价于 #for m in range(len(li)-2): if li[m]> li[m+1]: temp = li[m+1] li[m+1] = li[m] li[m] = temp for m in range(2): # 等价于 #for m in range(len(li)-3): if li[m]> li[m+1]: temp = li[m+1] li[m+1] = li[m] li[m] = temp for m in range(1): # 等价于 #for m in range(len(li)-4): if li[m]> li[m+1]: temp = li[m+1] li[m+1] = li[m] li[m] = temp print li #第三步 li = [13, 22, 6, 99, 11] for i in range(1,5): for m in range(len(li)-i): if li[m] > li[m+1]: temp = li[m+1] li[m+1] = li[m] li[m] = temp
九、匿名函数
匿名函数就是不需要显式的指定函数
def calc(n): return n**n print(calc(10)) #换成匿名函数 calc = lambda n:n**n print(calc(10))
匿名函数主要是和其它函数搭配使用的呢,如下
l=[3,2,100,999,213,1111,31121,333] print(max(l)) dic={'k1':10,'k2':100,'k3':30} print(max(dic)) print(dic[max(dic,key=lambda k:dic[k])]) res = map(lambda x:x**2,[1,5,7,4,8]) for i in res: print(i) #输出 #1 #25 #49 #16 #64
十、函数式编程
高阶函数:满足俩个特性任意一个即为高阶函数
- 函数的传入参数是一个函数名
- 函数的返回值是一个函数名
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 普通条件语句 if 1 == 1: name = 'zhangsan' else: name = 'lisi' # 三元运算 name = 'zhangsan' if 1 == 1 else 'lisi'
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# ###################### 普通函数 ###################### # 定义函数(普通方式) def func(arg): return arg + 1 # 执行函数 result = func(123) # ###################### lambda ###################### # 定义函数(lambda表达式) my_lambda = lambda arg: arg + 1 # 执行函数 result = my_lambda(123)
map
遍历序列,对序列中每个元素进行操作,最终获取新的序列。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
li = [11, 22, 33] new_list = map(lambda a: a + 100, li)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
li = [11, 22, 33] sl = [1, 2, 3] new_list = map(lambda a, b: a + b, li, sl)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
array=[1,3,4,71,2] ret=[] for i in array: ret.append(i**2) print(ret) #如果我们有一万个列表,那么你只能把上面的逻辑定义成函数 def map_test(array): ret=[] for i in array: ret.append(i**2) return ret print(map_test(array)) #如果我们的需求变了,不是把列表中每个元素都平方,还有加1,减一,那么可以这样 def add_num(x): return x+1 def map_test(func,array): ret=[] for i in array: ret.append(func(i)) return ret print(map_test(add_num,array)) #可以使用匿名函数 print(map_test(lambda x:x-1,array)) #上面就是map函数的功能,map得到的结果是可迭代对象 print(map(lambda x:x-1,range(5)))
filter
对于序列中的元素进行筛选,最终获取符合条件的序列
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#filter第一个参数为空,将获取原来序列 li = [11, 22, 33] new_list = filter(lambda arg: arg > 22, li)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python #_*_ coding:utf-8 _*_ #电影院聚集了一群看电影bb的傻逼,让我们找出他们 movie_people=['alex','wupeiqi','yuanhao','sb_alex','sb_wupeiqi','sb_yuanhao'] def tell_sb(x): return x.startswith('sb') def filter_test(func,array): ret=[] for i in array: if func(i): ret.append(i) return ret print(filter_test(tell_sb,movie_people)) #函数filter,返回可迭代对象 print(filter(lambda x:x.startswith('sb'),movie_people))
reduce
对于序列内所有元素进行累计操作
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# reduce的第一个参数,函数必须要有两个参数 # reduce的第二个参数,要循环的序列 # reduce的第三个参数,初始值 li = [11, 22, 33] result = reduce(lambda arg1, arg2: arg1 + arg2, li)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python #_*_ coding:utf-8 _*_ from functools import reduce #合并,得一个合并的结果 array_test=[1,2,3,4,5,6,7] array=range(100) #报错啊,res没有指定初始值 def reduce_test(func,array): l=list(array) for i in l: res=func(res,i) return res # print(reduce_test(lambda x,y:x+y,array)) #可以从列表左边弹出第一个值 def reduce_test(func,array): l=list(array) res=l.pop(0) for i in l: res=func(res,i) return res print(reduce_test(lambda x,y:x+y,array)) #我们应该支持用户自己传入初始值 def reduce_test(func,array,init=None): l=list(array) if init is None: res=l.pop(0) else: res=init for i in l: res=func(res,i) return res print(reduce_test(lambda x,y:x+y,array)) print(reduce_test(lambda x,y:x+y,array,50))
map,filter,reduce小结
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python #_*_ coding:utf-8 _*_ #当然了,map,filter,reduce,可以处理所有数据类型 name_dic=[ {'name':'alex','age':1000}, {'name':'wupeiqi','age':10000}, {'name':'yuanhao','age':9000}, {'name':'linhaifeng','age':18}, ] #利用filter过滤掉千年王八,万年龟,还有一个九千岁 def func(x): age_list=[1000,10000,9000] return x['age'] not in age_list res=filter(func,name_dic) for i in res: print(i) res=filter(lambda x:x['age'] == 18,name_dic) for i in res: print(i) #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))) #用map来处理字符串列表啊,把列表中所有人都变成sb,比方alex_sb name=['alex','wupeiqi','yuanhao'] res=map(lambda x:x+'_sb',name) for i in res: print(i)
十一、内置函数
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python #_*_ coding:utf-8 _*_ 字典的运算:最小值,最大值,排序 salaries={ 'egon':3000, 'alex':100000000, 'wupeiqi':10000, 'yuanhao':2000 } 迭代字典,取得是key,因而比较的是key的最大和最小值 >>> max(salaries) 'yuanhao' >>> min(salaries) 'alex' 可以取values,来比较 >>> max(salaries.values()) >>> min(salaries.values()) 但通常我们都是想取出,工资最高的那个人名,即比较的是salaries的值,得到的是键 >>> max(salaries,key=lambda k:salary[k]) 'alex' >>> min(salaries,key=lambda k:salary[k]) 'yuanhao' 也可以通过zip的方式实现 salaries_and_names=zip(salaries.values(),salaries.keys()) 先比较值,值相同则比较键 >>> max(salaries_and_names) (100000000, 'alex')