Python函數(shù)式介紹一 - 高階函數(shù)
Python函數(shù)式介紹二 - 鏈?zhǔn)秸{(diào)用
最近為了給朋友推廣Python函數(shù)式編程,特意準(zhǔn)備寫(xiě)一系列文章,當(dāng)然我不敢說(shuō)自己已經(jīng)把函數(shù)式琢磨透了,我覺(jué)得在我在函數(shù)式編程這道路上還有很長(zhǎng)的路需要走。
我們還是從高階函數(shù)入手吧,比較容易突破。
在這里我們會(huì)用到lambda函數(shù)也就是匿名函數(shù),我們就理解成是沒(méi)有名字的函數(shù)把。
map
students = [
{
"name":"wwb",
"sex":"1",
"course":[
{
"name":"Math",
"score":90
},
{
"name":"English",
"score":80
}
]
},
{
"name":"wxa",
"sex":"1",
"course":[
{
"name":"Music",
"score":90
},
{
"name":"English",
"score":80
}
]
},
{
"name":"wxb",
"sex":"1",
"course":[
{
"name":"Math",
"score":90
},
{
"name":"Music",
"score":80
}
]
},
]
以上是我們需要測(cè)試的數(shù)據(jù),我們做一個(gè)稍微簡(jiǎn)單的需求,求選修數(shù)學(xué)的學(xué)生。
先一步一步來(lái),把問(wèn)題分解,看以下數(shù)據(jù)
_course = [
{
"name":"Math",
"score":90
},
{
"name":"Music",
"score":80
}
]
求_course各個(gè)課程是否為Math,只需要一行
print(list(map(lambda it:it["name"]=="Math",_course)))
'''
結(jié)果:
[True,False]
'''
any,all
先看結(jié)果
print(any(map(lambda it:it["name"]=="Math",_course)))
print(all(map(lambda it:it["name"]=="Math",_course)))
print(any((True,True,True,False)))
print(any((False,False)))
print(all((True,True,True)))
print(all((True,False)))
'''
結(jié)果:
True
False
True
False
True
False
'''
any函數(shù)傳入的集合中一個(gè)為T(mén)rue則any結(jié)果為T(mén)ure,若集合全為Flase則結(jié)果為False
all函數(shù)傳入的集合必須所有都為T(mén)rue結(jié)果才為T(mén)rue,否則為False
求_course中是否包含Math,只需要一行
print(any(map(lambda it:it["name"]=="Math",_course)))
'''
結(jié)果:
True
'''
惰性求值
請(qǐng)看以下例子
print("test1")
print(map(lambda it:it["name"]=="Math",_course))
print("test2")
print(list(map(lambda it:it["name"]=="Math",_course)))
def fun0(it):
print(it)
return it["name"]=="Math"
print("test3")
"執(zhí)行所有操作"
print(any(list(map(fun0,_course))))
print("test4")
"map并沒(méi)有執(zhí)行完所有元素,遇到一個(gè)true直接返回"
print(any(map(fun0,_course)))
'''
結(jié)果:
test1
<map object at 0x0000000004CD4400>
test2
[True, False]
test3
{'name': 'Math', 'score': 90}
{'name': 'Music', 'score': 80}
True
test4
{'name': 'Math', 'score': 90}
True
'''
我們直接把map結(jié)果打印出來(lái)會(huì)發(fā)現(xiàn)是一個(gè)map對(duì)象,事實(shí)上這個(gè)對(duì)象還沒(méi)有做任何操作,如果我們對(duì)map對(duì)象執(zhí)行l(wèi)ist操作,這個(gè)時(shí)候map處理集合的數(shù)據(jù)。test4可以發(fā)現(xiàn)any一旦遇到True就直接返回了,剩下的元素map不會(huì)去處理。
這個(gè)特性叫惰性求值,當(dāng)我們真的需要map object的結(jié)果的時(shí)候,map才會(huì)開(kāi)始對(duì)元素進(jìn)行加工處理。而且是需要map object一個(gè)元素,map就處理一個(gè)元素,一個(gè)都不會(huì)處理多。這個(gè)就保證了不會(huì)浪費(fèi)cpu的資源,當(dāng)我map一個(gè)1000個(gè)元素的集合,實(shí)際上我只取其中的一個(gè)返回值,則python是不會(huì)浪費(fèi)cpu去處理剩下的999個(gè)元素的。
如果你list一個(gè)map對(duì)象,則map會(huì)操作集合的所有元素,返回一個(gè)結(jié)果集合??催@個(gè)是不是就是數(shù)學(xué)映射的概念?一個(gè)集合轉(zhuǎn)化成另一個(gè)一一對(duì)應(yīng)的集合。所以python把這個(gè)操作取名為map也就是映射的意思。
filter
filter函數(shù)和它的名字一樣做的是過(guò)濾的操作,在一個(gè)集合中篩選出符合條件的數(shù)據(jù)
求選修數(shù)學(xué)的同學(xué)
def findmath(courses):
return any(map(lambda it:it["name"]=="Math",courses))
print(list(filter(lambda it:findmath(it["course"]),students)))
'''
結(jié)果:
[
{
'name': 'wwb',
'sex': '1',
'course':
[
{'name': 'Math', 'score': 90},
{'name':'English', 'score': 80}
]
},
{
'name': 'wxb',
'sex': '1',
'course': [
{'name': 'Math', 'score': 90 },
{'name': 'Music','score': 80 }
]
}
]
'''
reduce
先看一個(gè)簡(jiǎn)單的例子
from functools import reduce
ilist = [1,2,3,4,5,6,7,8,9,10]
print(reduce(lambda acc,it:acc+it,ilist,0))
print(reduce(lambda acc,it:acc+it,ilist))
'''
結(jié)果:
55
55
'''
reduce(lambda acc,it:acc+it,ilist,0)
一開(kāi)始acc為0,it為列表第一項(xiàng)ilist[0],
接著 acc為 (ilist[0] + 0) ,it 為 ilist[1]
接著 acc為 ((ilist[0] + 0) + ilist[1]),it 為 ilist[2]
接著 acc為 (((ilist[0] + 0) + ilist[1])+ilist[2]),it 為 ilist[3]
...
直到最后一項(xiàng),返回最后一次運(yùn)行的結(jié)果。
一般我稱(chēng)acc為累計(jì)值,0為幺元,幺元是一個(gè)數(shù)學(xué)概念,是相對(duì)運(yùn)算而來(lái)的,比如
0是加減法的幺元,任何元素(+或-) 0 都等于本身
1是乘除法的幺元,任何元素(*或/) 1 都等于本身
""是字符串加法的幺元,任何字符串(除了NULL)加 "" 都等于本身
一般我都習(xí)慣在reduce函數(shù)加上幺元,reduce也支持不加幺元的情況,不加幺元?jiǎng)t,acc初始值為集合的第一個(gè)元素
reduce(lambda acc,it:acc+it,ilist)
一開(kāi)始acc為ilist[0],it為列表第一項(xiàng)ilist[1],
接著 acc為 (ilist[0] + ilist[1]),it 為 ilist[2]
...
直到最后一項(xiàng),返回最后一次運(yùn)行的結(jié)果。
如果不傳幺元?jiǎng)t當(dāng)列表ilist有0個(gè)元素的時(shí)候,reduce函數(shù)會(huì)報(bào)錯(cuò)
統(tǒng)計(jì)選修數(shù)學(xué)平均分
我們簡(jiǎn)化下問(wèn)題,不然又再學(xué)幾個(gè)新函數(shù),我們就用上面學(xué)過(guò)的map/filter/any/reduce函數(shù)
以下是選修數(shù)學(xué)的學(xué)生的數(shù)學(xué)成績(jī)
studentmaths=[
{
"name":"wwb",
"sex":1,
"score":90,
},
{
"name":"wxa",
"sex":1,
"score":91,
},
{
"name":"wxb",
"sex":0,
"score":92,
},
{
"name":"wxc",
"sex":0,
"score":93,
},
{
"name":"wxd",
"sex":0,
"score":94,
},
]
求總分
print(reduce(lambda acc,it:acc+it["score"],studentmaths,0))
'''
結(jié)果:
460
'''
求平均分
print(reduce(lambda acc,it:acc+it["score"],studentmaths,0)/len(studentmaths))
'''
結(jié)果:
92.0
'''
打印班上所有同學(xué)的成績(jī)
print(reduce(lambda acc,it:acc + "name:%s,score:%d\r\n"%(it["name"],it["score"]),studentmaths,""))
'''
結(jié)果:
name:wwb,score:90
name:wxa,score:91
name:wxb,score:92
name:wxc,score:93
name:wxd,score:94
'''
我們來(lái)看一個(gè)復(fù)雜的幺元,求女生平均分
temp = reduce(lambda acc,it: { "sum":acc["sum"]+it["score"],"count":acc["count"]+1},filter(lambda s:s["sex"]==0,studentmaths),{"sum":0,"count":0})
print(temp)
print(temp["sum"]/temp["count"])
'''
結(jié)果:
{'sum': 279, 'count': 3}
93.0
'''
當(dāng)然可以更簡(jiǎn)單些,以上寫(xiě)法是為了解釋幺元,展示reduce的復(fù)雜寫(xiě)法,其實(shí)就這個(gè)例子而論,沒(méi)什么必要這么寫(xiě),可以寫(xiě)成以下簡(jiǎn)單的寫(xiě)法
fstudentmaths = list(filter(lambda s:s["sex"]==0,studentmaths))
print(reduce(lambda acc,it:acc+it["score"],fstudentmaths,0)/len(fstudentmaths))
'''
結(jié)果:
93.0
'''
打印男生成績(jī)單
print(reduce(lambda acc,it:acc + "name:%s,score:%d\r\n"%(it["name"],it["score"]),filter(lambda s:s["sex"]==1,studentmaths),""))
'''
結(jié)果:
name:wwb,score:90
name:wxa,score:91
'''
總結(jié)
利用python自帶的map/filter/reduce/all/any函數(shù)我們可以改寫(xiě)大部分for/while循環(huán)。我們利用python的這個(gè)高級(jí)函數(shù)你會(huì)發(fā)現(xiàn)你的思維方式會(huì)發(fā)生改變,你會(huì)習(xí)慣這種集合轉(zhuǎn)化來(lái)轉(zhuǎn)化去的思維。
而for/while則是串行的,逐個(gè)處理的思想。自己寫(xiě)for/while其實(shí)很容易出錯(cuò)的,用高級(jí)函數(shù)都是一些簡(jiǎn)單的lambda函數(shù)組合在一起,想出錯(cuò)也不太容易。
我個(gè)人認(rèn)為程序結(jié)構(gòu),順序、條件、循環(huán),循環(huán)是最復(fù)雜的結(jié)構(gòu),如果一段程序中嵌套三四個(gè)循環(huán),每個(gè)循環(huán)幾百行,那這段代碼基本不用看了。高階函數(shù)是用集合的觀點(diǎn)來(lái)處理數(shù)據(jù)的,化循環(huán)為順序結(jié)構(gòu),能讓程序更容易理解
另外,大家有沒(méi)有發(fā)現(xiàn)最后面函數(shù)寫(xiě)得很長(zhǎng)很長(zhǎng),而且很不好閱讀,沒(méi)關(guān)系,下節(jié)我們稍微加工一下,變成鏈?zhǔn)秸{(diào)用方式
測(cè)試代碼
我直接把上面的測(cè)試代碼貼出來(lái)吧
from functools import reduce
students = [
{
"name":"wwb",
"sex":"1",
"course":[
{
"name":"Math",
"score":90
},
{
"name":"English",
"score":80
}
]
},
{
"name":"wxa",
"sex":"1",
"course":[
{
"name":"Music",
"score":90
},
{
"name":"English",
"score":80
}
]
},
{
"name":"wxb",
"sex":"1",
"course":[
{
"name":"Math",
"score":90
},
{
"name":"Music",
"score":80
}
]
},
]
_course = [
{
"name":"Math",
"score":90
},
{
"name":"Music",
"score":80
}
]
"是否選修數(shù)學(xué)課"
print(map(lambda it:it["name"]=="Math",_course))
"顯示結(jié)果"
print(list(map(lambda it:it["name"]=="Math",_course)))
"all,any"
print(any(map(lambda it:it["name"]=="Math",_course)))
print(all(map(lambda it:it["name"]=="Math",_course)))
"惰性求值測(cè)試"
def fun0(it):
print(it)
return it["name"]=="Math"
"執(zhí)行所有操作"
print(any(list(map(fun0,_course))))
"map并沒(méi)有執(zhí)行完所有元素,遇到一個(gè)true直接返回"
print(any(map(fun0,_course)))
"filter"
"找出選修數(shù)學(xué)的所有學(xué)生"
def findmath(courses):
return any(map(lambda it:it["name"]=="Math",courses))
print(list(filter(lambda it:findmath(it["course"]),students)))
"reduce"
"統(tǒng)計(jì)選修數(shù)學(xué)平均分"
"以下是選修數(shù)學(xué)的學(xué)生的數(shù)學(xué)成績(jī)"
studentmaths=[
{
"name":"wwb",
"sex":1,
"score":90,
},
{
"name":"wxa",
"sex":1,
"score":91,
},
{
"name":"wxb",
"sex":0,
"score":92,
},
{
"name":"wxc",
"sex":0,
"score":93,
},
{
"name":"wxd",
"sex":0,
"score":94,
},
]
"reduce簡(jiǎn)單例子"
ilist = [1,2,3,4,5,6,7,8,9,10]
print(reduce(lambda acc,it:acc+it,ilist,0))
print(reduce(lambda acc,it:acc+it,ilist))
"總分"
print(reduce(lambda acc,it:acc+it["score"],studentmaths,0))
"平均分"
print(reduce(lambda acc,it:acc+it["score"],studentmaths,0)/len(studentmaths))
"把班上同學(xué)的成績(jī)打印出來(lái)"
print(reduce(lambda acc,it:acc + "name:%s,score:%d\r\n"%(it["name"],it["score"]),studentmaths,""))
"女生平均分"
temp = reduce(lambda acc,it: { "sum":acc["sum"]+it["score"],"count":acc["count"]+1},filter(lambda s:s["sex"]==0,studentmaths),{"sum":0,"count":0})
print(temp)
print(temp["sum"]/temp["count"])
"更簡(jiǎn)單些寫(xiě)法"
fstudentmaths = list(filter(lambda s:s["sex"]==0,studentmaths))
print(reduce(lambda acc,it:acc+it["score"],fstudentmaths,0)/len(fstudentmaths))
"男生成績(jī)單"
print(reduce(lambda acc,it:acc + "name:%s,score:%d\r\n"%(it["name"],it["score"]),filter(lambda s:s["sex"]==1,studentmaths),""))