Python函數(shù)式介紹一 - 高階函數(shù)
Python函數(shù)式介紹二 - 鏈?zhǔn)秸{(diào)用
最近為了給朋友推廣Python函數(shù)式編程硼婿,特意準(zhǔn)備寫一系列文章出皇,當(dāng)然我不敢說自己已經(jīng)把函數(shù)式琢磨透了肴捉,我覺得在我在函數(shù)式編程這道路上還有很長的路需要走痢站。
我們還是從高階函數(shù)入手吧吻贿,比較容易突破鳍怨。
在這里我們會用到lambda函數(shù)也就是匿名函數(shù)呻右,我們就理解成是沒有名字的函數(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
}
]
},
]
以上是我們需要測試的數(shù)據(jù)鞋喇,我們做一個(gè)稍微簡單的需求声滥,求選修數(shù)學(xué)的學(xué)生。
先一步一步來侦香,把問題分解落塑,看以下數(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è)為True則any結(jié)果為Ture,若集合全為Flase則結(jié)果為False
all函數(shù)傳入的集合必須所有都為True結(jié)果才為True罐韩,否則為False
求_course中是否包含Math
,只需要一行
print(any(map(lambda it:it["name"]=="Math",_course)))
'''
結(jié)果:
True
'''
惰性求值
請看以下例子
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并沒有執(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é)果打印出來會發(fā)現(xiàn)是一個(gè)map對象憾赁,事實(shí)上這個(gè)對象還沒有做任何操作,如果我們對map對象執(zhí)行l(wèi)ist操作散吵,這個(gè)時(shí)候map處理集合的數(shù)據(jù)龙考。test4可以發(fā)現(xiàn)any一旦遇到True就直接返回了蟆肆,剩下的元素map不會去處理。
這個(gè)特性叫惰性求值晦款,當(dāng)我們真的需要map object的結(jié)果的時(shí)候炎功,map才會開始對元素進(jìn)行加工處理。而且是需要map object一個(gè)元素柬赐,map就處理一個(gè)元素亡问,一個(gè)都不會處理多。這個(gè)就保證了不會浪費(fèi)cpu的資源肛宋,當(dāng)我map一個(gè)1000個(gè)元素的集合州藕,實(shí)際上我只取其中的一個(gè)返回值,則python是不會浪費(fèi)cpu去處理剩下的999個(gè)元素的酝陈。
如果你list一個(gè)map對象床玻,則map會操作集合的所有元素,返回一個(gè)結(jié)果集合沉帮⌒馑溃看這個(gè)是不是就是數(shù)學(xué)映射
的概念?一個(gè)集合轉(zhuǎn)化成另一個(gè)一一對應(yīng)的集合穆壕。所以python把這個(gè)操作取名為map
也就是映射
的意思待牵。
filter
filter函數(shù)和它的名字一樣做的是過濾的操作,在一個(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è)簡單的例子
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)
一開始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é)果川背。
一般我稱acc為累計(jì)值贰拿,0為幺元,幺元是一個(gè)數(shù)學(xué)概念熄云,是相對運(yùn)算而來的膨更,比如
0是加減法的幺元,任何元素(+或-) 0 都等于本身
1是乘除法的幺元缴允,任何元素(*或/) 1 都等于本身
""是字符串加法的幺元荚守,任何字符串(除了NULL)加 "" 都等于本身
一般我都習(xí)慣在reduce函數(shù)加上幺元,reduce也支持不加幺元的情況练般,不加幺元?jiǎng)t矗漾,acc初始值為集合的第一個(gè)元素
reduce(lambda acc,it:acc+it,ilist)
一開始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ù)會報(bào)錯(cuò)
統(tǒng)計(jì)選修數(shù)學(xué)平均分
我們簡化下問題,不然又再學(xué)幾個(gè)新函數(shù),我們就用上面學(xué)過的map/filter/any/reduce函數(shù)
以下是選修數(shù)學(xué)的學(xué)生的數(shù)學(xué)成績
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é)的成績
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
'''
我們來看一個(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)然可以更簡單些,以上寫法是為了解釋幺元琳钉,展示reduce的復(fù)雜寫法,其實(shí)就這個(gè)例子而論势木,沒什么必要這么寫,可以寫成以下簡單的寫法
fstudentmaths = list(filter(lambda s:s["sex"]==0,studentmaths))
print(reduce(lambda acc,it:acc+it["score"],fstudentmaths,0)/len(fstudentmaths))
'''
結(jié)果:
93.0
'''
打印男生成績單
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ù)我們可以改寫大部分for/while循環(huán)歌懒。我們利用python的這個(gè)高級函數(shù)你會發(fā)現(xiàn)你的思維方式會發(fā)生改變啦桌,你會習(xí)慣這種集合轉(zhuǎn)化來轉(zhuǎn)化去的思維。
而for/while則是串行的及皂,逐個(gè)處理的思想甫男。自己寫for/while其實(shí)很容易出錯(cuò)的,用高級函數(shù)都是一些簡單的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)來處理數(shù)據(jù)的婆跑,化循環(huán)為順序結(jié)構(gòu),能讓程序更容易理解
另外谱秽,大家有沒有發(fā)現(xiàn)最后面函數(shù)寫得很長很長洽蛀,而且很不好閱讀,沒關(guān)系疟赊,下節(jié)我們稍微加工一下郊供,變成鏈?zhǔn)秸{(diào)用方式
測試代碼
我直接把上面的測試代碼貼出來吧
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)))
"惰性求值測試"
def fun0(it):
print(it)
return it["name"]=="Math"
"執(zhí)行所有操作"
print(any(list(map(fun0,_course))))
"map并沒有執(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é)成績"
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簡單例子"
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é)的成績打印出來"
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"])
"更簡單些寫法"
fstudentmaths = list(filter(lambda s:s["sex"]==0,studentmaths))
print(reduce(lambda acc,it:acc+it["score"],fstudentmaths,0)/len(fstudentmaths))
"男生成績單"
print(reduce(lambda acc,it:acc + "name:%s,score:%d\r\n"%(it["name"],it["score"]),filter(lambda s:s["sex"]==1,studentmaths),""))