一、为何使用函数
背景提要:在学习函数之前,一直遵循:面向过程编程,即:根据业务逻辑从上到下实现功能,其往往用一长段代码来实现指定功能,开发过程中最常见的操作就是粘贴复制,也就是将之前实现的代码块复制到现需功能处,如下:
whileTrue:if cpu利用率 > 90%:#发送邮件提醒
连接邮箱服务器
发送邮件
关闭连接if 硬盘使用空间 > 90%:#发送邮件提醒
连接邮箱服务器
发送邮件
关闭连接if 内存占用 > 80%:#发送邮件提醒
连接邮箱服务器
发送邮件
关闭连接
改进:把重复的代码提取出来,放在一个公共的地方,起个名字,以后谁想用这段代码,就通过这个名字调用就行了,如下:
def发送邮件(内容)#发送邮件提醒
连接邮箱服务器
发送邮件
关闭连接whileTrue:if cpu利用率 > 90%:
发送邮件('CPU报警')if 硬盘使用空间 > 90%:
发送邮件('硬盘报警')if 内存占用 > 80%:
发送邮件('内存报警')
对于上述的两种实现方式,第二次必然比第一次的重用性和可读性要好,其实这就是函数式编程和面向过程编程的区别:
函数式:将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可
面向对象:对函数进行分类和封装,让开发“更快更好更强...”
二、定义函数的方法
函数是逻辑结构化和过程化的一种编程方法;函数式编程最重要的是增强代码的重用性和可读性。
#python中函数定义方法:
deftest(x):"The function definitions"x+=1
returnx
test(3)#def:定义函数的关键字#test:函数名#():内可定义形参#"":文档描述(非必要,但是强烈建议为你的函数添加描述信息)#x+=1:函数体(泛指代码块或程序处理逻辑)#return:函数体(定义返回值)#调用运行:可以带参数也可以不带#函数名()
1、返回值
函数是一个功能块,该功能到底执行成功与否,需要通过返回值来告知调用者,没有返回值的函数也称为过程。
#!/usr/bin/env python#_*_ coding:utf-8 _*_
deftest01():
msg='hello The little green frog'
printmsgdeftest02():
msg='hello WuDaLang'
printmsgreturnmsg
t1=test01()
t2=test02()print 'from test01 return is [%s]' %t1print '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的形式参数,简称:形参
deffunc(name):printname########## 执行函数 ##########'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): #将参数包装成列表传入函数
printargs
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): #将参数包装成字典传入函数
printkargs
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):printargsprint kwargs
importsmtplibfrom email.mime.text importMIMETextfrom email.utils importformataddr
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中所有的名字都没有储值功能,函数是第一个对象。
deffoo():print("yyp")
foo()#输出结果:
yyp
可以当做参数
deffoo():print "tom"
deffunc(msg):printmsg
msg()
func(foo)#输出结果:
tom
可以当做返回值
deffoo():print "tom"
deffunc(msg):returnmsg
f=func(foo)printf
f()#输出结果:##tom
可以当做容器类型的一个元素
deffoo():return "tom"func_dic={'foo':foo()}print func_dic.get('foo')#输出结果:#tom
一类对象指的是:函数可以被当做数据来处理被引用
四、局部变量和全局变量
在子程序中定义的变量称为局部变量,在程序的一开始定义的变量称为全局变量。
全局变量作用域是整个程序,局部变量作用域是定义该变量的子程序。
当全局变量与局部变量同名时:在定义局部变量的子程序内,局部变量起作用;在其它地方全局变量起作用。
name='lhf' #全局变量
defchange_name():print('我的名字',name)
change_name()defchange_name():
name='帅了一笔' #局部变量
print('我的名字',name)
change_name()print(name)defchange_name():globalname
name='帅了一笔'
print('我的名字',name)
change_name()print(name)
五、前向引用之-函数即变量
defaction():print 'in the action'logger()
action()#报错NameError: global name 'logger' is not defined
deflogger():print 'in the logger'
defaction():print 'in the action'logger()
action()
六、嵌套函数和作用域
name = "yyp"
defchange_name():
name= "yyp2"
defchange_name2():
name= "yyp3"
print("第3层打印",name)
change_name2()#调用内层函数
print("第2层打印",name)
change_name()print("最外层打印",name)
作用域在定义函数时就已经固定住了,不会随着调用位置的改变而改变,此时,在最外层调用change_name2()会出错。
#例一:
name='yyp'
deffoo():
name='sy'
defbar():print(name)returnbar
func=foo()
func()
例二:
name='yyp'
deffoo():
name='sy'
defbar():
name='tom'
deftt():print(name)returnttreturnbar
func=foo()
func()()
七、递归调用
递归特性:
必须有一个明确的结束条件
每次进入更深一层递归时,问题规模相比上次递归都应有所减少
递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出)
defcalc(n):print(n)if int(n/2) ==0:returnnreturn calc(int(n/2))
calc(10)#输出:#10#5#2
#!/usr/bin/env python#_*_ coding:utf-8 _*_
importtime
person_list=['alex','wupeiqi','yuanhao','linhaifeng']defask_way(person_list):print('-'*60)if len(person_list) ==0:return '没人知道'person=person_list.pop(0)if person == 'linhaifeng':return '%s说:我知道,老男孩就在沙河汇德商厦,下地铁就是' %personprint('hi 美男[%s],敢问路在何方' %person)print('%s回答道:我不知道,但念你慧眼识猪,你等着,我帮你问问%s...' %(person,person_list))
time.sleep(3)
res=ask_way(person_list)#print('%s问的结果是: %res' %(person,res))
returnres
res=ask_way(person_list)print(res)
递归问路
data = [1, 3, 6, 7, 9, 12, 14, 16, 17, 18, 20, 21, 22, 23, 30, 32, 33, 35]defbinary_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])returnbinary_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] 进行排序
思路:相邻两个值进行比较,将较大的值放在右侧,依次比较!
#第一步
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]=tempfor 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]=tempfor 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]=tempfor 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]=tempprintli#第三步
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
冒泡算法
九、匿名函数
匿名函数就是不需要显式的指定函数
defcalc(n):return n**nprint(calc(10))#换成匿名函数
calc = lambda n:n**nprint(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=lambdak:dic[k])])
res= map(lambda x:x**2,[1,5,7,4,8])for i inres:print(i)#输出#1#25#49#16#64
十、函数式编程
高阶函数:满足俩个特性任意一个即为高阶函数
函数的传入参数是一个函数名
函数的返回值是一个函数名
#普通条件语句
if 1 == 1:
name= 'zhangsan'
else:
name= 'lisi'
#三元运算
name = 'zhangsan' if 1 == 1 else 'lisi'
三元运算
####################### 普通函数 #######################定义函数(普通方式)
deffunc(arg):return arg + 1
#执行函数
result = func(123)####################### lambda #######################定义函数(lambda表达式)
my_lambda = lambda arg: arg + 1
#执行函数
result = my_lambda(123)
lambda表达式
map
遍历序列,对序列中每个元素进行操作,最终获取新的序列。
li = [11, 22, 33]
new_list= map(lambda a: a + 100, li)
每个元素增加100
li = [11, 22, 33]
sl= [1, 2, 3]
new_list= map(lambda a, b: a + b, li, sl)
两个列表对应元素相加
array=[1,3,4,71,2]
ret=[]for i inarray:
ret.append(i**2)print(ret)#如果我们有一万个列表,那么你只能把上面的逻辑定义成函数
defmap_test(array):
ret=[]for i inarray:
ret.append(i**2)returnretprint(map_test(array))#如果我们的需求变了,不是把列表中每个元素都平方,还有加1,减一,那么可以这样
defadd_num(x):return x+1
defmap_test(func,array):
ret=[]for i inarray:
ret.append(func(i))returnretprint(map_test(add_num,array))#可以使用匿名函数
print(map_test(lambda x:x-1,array))#上面就是map函数的功能,map得到的结果是可迭代对象
print(map(lambda x:x-1,range(5)))
map函数
filter
对于序列中的元素进行筛选,最终获取符合条件的序列
#filter第一个参数为空,将获取原来序列
li = [11, 22, 33]
new_list= filter(lambda arg: arg > 22, li)
获取列表中大于12的所有元素集合
#!/usr/bin/env python#_*_ coding:utf-8 _*_
#电影院聚集了一群看电影bb的傻逼,让我们找出他们
movie_people=['alex','wupeiqi','yuanhao','sb_alex','sb_wupeiqi','sb_yuanhao']deftell_sb(x):return x.startswith('sb')deffilter_test(func,array):
ret=[]for i inarray:iffunc(i):
ret.append(i)returnretprint(filter_test(tell_sb,movie_people))#函数filter,返回可迭代对象
print(filter(lambda x:x.startswith('sb'),movie_people))
filter函数
reduce
对于序列内所有元素进行累计操作
#reduce的第一个参数,函数必须要有两个参数#reduce的第二个参数,要循环的序列#reduce的第三个参数,初始值
li= [11, 22, 33]
result= reduce(lambda arg1, arg2: arg1 + arg2, li)
获取序列所有元素的和
#!/usr/bin/env python#_*_ coding:utf-8 _*_
from functools importreduce#合并,得一个合并的结果
array_test=[1,2,3,4,5,6,7]
array=range(100)#报错啊,res没有指定初始值
defreduce_test(func,array):
l=list(array)for i inl:
res=func(res,i)returnres#print(reduce_test(lambda x,y:x+y,array))
#可以从列表左边弹出第一个值
defreduce_test(func,array):
l=list(array)
res=l.pop(0)for i inl:
res=func(res,i)returnresprint(reduce_test(lambda x,y:x+y,array))#我们应该支持用户自己传入初始值
def reduce_test(func,array,init=None):
l=list(array)if init isNone:
res=l.pop(0)else:
res=initfor i inl:
res=func(res,i)returnresprint(reduce_test(lambda x,y:x+y,array))print(reduce_test(lambda x,y:x+y,array,50))
reduce函数
map,filter,reduce小结
#!/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过滤掉千年王八,万年龟,还有一个九千岁
deffunc(x):
age_list=[1000,10000,9000]return x['age'] not inage_list
res=filter(func,name_dic)for i inres:print(i)
res=filter(lambda x:x['age'] == 18,name_dic)for i inres:print(i)#reduce用来计算1到100的和
from functools importreduceprint(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 inres:print(i)
总结
十一、内置函数
#!/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=lambdak:salary[k])'alex'
>>> min(salaries,key=lambdak:salary[k])'yuanhao'也可以通过zip的方式实现
salaries_and_names=zip(salaries.values(),salaries.keys())
先比较值,值相同则比较键>>>max(salaries_and_names)
(100000000, 'alex')
View Code