Python函數(shù)式介紹一 - 高階函數(shù)
Python函數(shù)式介紹二 - 鏈?zhǔn)秸{(diào)用
上一篇文章中我們發(fā)現(xiàn)我們的代碼越來(lái)越長(zhǎng)了,而且都擠在一行,代碼越長(zhǎng),越不易讀违崇。括號(hào)一層嵌一層,真的容易繞暈诊霹。我一直覺(jué)得代碼要寫(xiě)給人看羞延,一直追求代碼即注釋這種程度的簡(jiǎn)單。
那有什么辦法來(lái)簡(jiǎn)化這個(gè)問(wèn)題畅哑?答案在標(biāo)題肴楷,鏈?zhǔn)秸{(diào)用。我們仿照C#LINQ的鏈?zhǔn)浇涌谲拧V苯由洗a赛蔫,這分代碼是我自己寫(xiě)的,很簡(jiǎn)單泥张。有興趣可以自己研究呵恢。里面封裝了我覺(jué)得比較重要的幾個(gè)高階函數(shù)。如果不夠的話(huà)也可以簡(jiǎn)單地再封裝一下媚创。如下渗钉,
#文件名FT.py
from functools import reduce
from collections import Iterator
from itertools import chain,groupby,product
class From:
src=None
def __init__(self,src):
self.src=src
def toList(self):
return list(self.src)
def toSet(self):
return set(self.src)
def toTuple(self):
return tuple(self.src)
def getSource(self):
return self.src
def map(self,func):
return From(map(func,self.src))
def filter(self,predicate):
return From(filter(predicate,self.src))
def reduce(self,func,identity=None):
if identity is None:
return reduce(func,self.src)
else:
return reduce(func,self.src,identity)
def chain(self):
return From(chain.from_iterable(self.src))
def groupby(self,func=None):
return From(map(lambda it:(it[0],list(it[1])),groupby(self.src,func)))
def product(self,tag):
return From(product(self.src,tag))
def all(self,predicate):
return all(map(lambda it:predicate(it),self.src))
def any(self,predicate):
return any(map(lambda it:predicate(it),self.src))
def first(self,predicate=None):
if predicate is None:
if isinstance(self.src,Iterator):
return next(self.src)
return next(iter(self.src))
else :
return next(filter(predicate,self.src))
def firstOrNone(self,predicate=None):
try:
if predicate is None:
if isinstance(self.src,Iterator):
return next(self.src)
return next(iter(self.src))
else :
return next(filter(predicate,self.src))
except StopIteration:
return None
以上代碼我寫(xiě)了一個(gè)From
類(lèi),后面的代碼會(huì)反反復(fù)復(fù)地用到這個(gè)類(lèi)。
復(fù)習(xí)map/filter/reduce
例子1 鳄橘,過(guò)濾出列表中的偶數(shù)声离,再乘2
from FT import From
print(From((1,2,3,4))
.filter(lambda it:it % 2==0)
.map(lambda it:it*2).toList())
'''
結(jié)果:
[4,8]
'''
例子2,過(guò)濾出列表中地偶數(shù)瘫怜,結(jié)果乘2术徊,最后再求和。
from FT import From
print(From((1,2,3,4))
.filter(lambda it:it % 2==0)
.map(lambda it:it*2).reduce(lambda acc,it:acc+it))
#加幺元
print(From((1,2,3,4))
.filter(lambda it:it % 2==0)
.map(lambda it:it*2).reduce(lambda acc,it:acc+it))
'''
結(jié)果:
12
12
'''
對(duì)比上篇文章鲸湃,有沒(méi)有覺(jué)得清晰很多赠涮,清晰地看到數(shù)據(jù)流一步一步地往下一個(gè)函數(shù)流。這個(gè)也是函數(shù)式地特點(diǎn)之一暗挑。python本不提供這個(gè)流式接口笋除,沒(méi)關(guān)系,我們自己造一個(gè)炸裆,而且沒(méi)有花幾行代碼垃它。居然全部代碼都能在一篇文章中顯示出來(lái),不過(guò)這個(gè)跟python自己動(dòng)態(tài)語(yǔ)言有關(guān)晒衩,若換成靜態(tài)語(yǔ)言的話(huà)那應(yīng)該要花多不少功夫嗤瞎。下次找機(jī)會(huì)我用C++實(shí)現(xiàn)一遍。
下面我們學(xué)多幾個(gè)高階函數(shù)听系,豐富我們的武器庫(kù)。
groupby
分組是一個(gè)很常見(jiàn)的需求虹菲,需要的代碼其實(shí)也不少靠胜,封裝成高階函數(shù)后那方便太多了。請(qǐng)看下面的例子
testgroupdata=[{"id":1,"name":"wwb"},{"id":1,"name":"wxa"},{"id":1,"name":"wxb"},{"id":2,"name":"wxc"},{"id":2,"name":"wxd"}]
這個(gè)是數(shù)據(jù)集毕源。是我隨便造的浪漠,沒(méi)什么特殊意義。
例子1霎褐,根據(jù)id分組址愿,顯示分組
print(From(testgroupdata).groupby(lambda it:it['id']).toList())
'''
結(jié)果:
[
(
1,
[
{
'id': 1,
'name': 'wwb'
},
{
'id': 1,
'name': 'wxa'
},
{
'id': 1,
'name': 'wxb'
}
]
),
(
2,
[
{
'id': 2,
'name': 'wxc'
},
{
'id': 2,
'name': 'wxd'
}
]
)
]
'''
當(dāng)然groupby后可以自己接其他高階函數(shù),如下例子
例子2冻璃,根據(jù)id分組后响谓,過(guò)濾出分組的KEY為偶數(shù)的數(shù)據(jù),也就是id為偶數(shù)的數(shù)據(jù)
print(From(testgroupdata).groupby(lambda it:it['id']).filter(lambda it:it[0]%2==0).toList())
'''
結(jié)果:
[
(
2,
[
{
'id': 2,
'name': 'wxc'
},
{
'id': 2,
'name': 'wxd'
}
]
)
]
'''
笛卡爾積
笛卡兒積太重要了省艳,數(shù)據(jù)庫(kù)兩種表連接或join都可以解釋為笛卡兒積娘纷,看下面的例子。
例子1跋炕,觀察執(zhí)行結(jié)果
print(From((1,2,3)).product(('a','b','c')).toList())
現(xiàn)在我們考慮一個(gè)圖書(shū)館管理系統(tǒng)赖晶,先觀察以下數(shù)據(jù)集
students = [
{
"name":"wwb",
"book":[1,2,5]
},
{
"name":"wxa",
"book":[1,2,3]
},
{
"name":"wxb",
"book":[2,3,4]
}
]
books = [
{
"id":1,
"name":"C++ Primer"
},
{
"id":2,
"name":"Effecitve C++"
},
{
"id":3,
"name":"語(yǔ)文"
},{
"id":4,
"name":"數(shù)學(xué)"
},
{
"id":5,
"name":"英語(yǔ)"
}
]
students
是圖書(shū)館借了數(shù)的同學(xué),一個(gè)人可以借多本書(shū)辐烂,其中book
記錄的是圖書(shū)的id遏插;books
是圖書(shū)館里面的書(shū)∥婊撸現(xiàn)在可以看例子了。
例子2胳嘲,求學(xué)生具體借了什么書(shū)
print(From(students).map(lambda s:
{
"name":s["name"],
"book":From(s["book"]).product(books).filter(lambda it:it[0]==it[1]["id"])
.map(lambda it:it[1]["name"]).toList()
}).toList())
'''
結(jié)果:
[
{
'name': 'wwb',
'book': [
'C++ Primer',
'Effecitve C++',
'英語(yǔ)'
]
},
{
'name': 'wxa',
'book': [
'C++ Primer',
'Effecitve C++',
'語(yǔ)文'
]
},
{
'name': 'wxb',
'book': [
'Effecitve C++',
'語(yǔ)文',
'數(shù)學(xué)'
]
}
]
'''
first/firstOrNone
這兩個(gè)函數(shù)簡(jiǎn)單眷蜓,就是找出符合條件的第一條數(shù)據(jù)。我常常需要這樣的需求胎围。直接看例子
#找出名字為wwb的同學(xué)吁系,找不到的話(huà)則會(huì)拋出異常
print(From(students).first(lambda it:it["name"]=="wwb"))
#找出名字為wxx的同學(xué),找不到的話(huà)返回None
print(From(students).firstOrNone(lambda it:it["name"]=="wxx"))
'''
結(jié)果:
{'name': 'wwb', 'book': [1, 2, 5]}
None
'''
first當(dāng)找不到滿(mǎn)足條件的數(shù)據(jù)會(huì)拋異常白魂,而firstOrNone則會(huì)返回None汽纤。很簡(jiǎn)單。
chain
今天解釋最后一個(gè)函數(shù)福荸,chain蕴坪。這個(gè)是一個(gè)非常重要的函數(shù)。我自己經(jīng)常叫它扁平敬锐,用來(lái)變平數(shù)據(jù)背传。如
((a,b,c),(d,e,f),(g,h,i)) => (a,b,c,d,e,f,g,h,i)
經(jīng)過(guò)chain后,你會(huì)發(fā)現(xiàn)括號(hào)少了一層台夺,原來(lái)'尖'的數(shù)據(jù)現(xiàn)在變'平'了径玖,因?yàn)樯倭艘粚永ㄌ?hào)〔椋看例子
print(From(((1,2,3),(4,5,6),(1,2))).chain().toList())
'''
結(jié)果:
[1, 2, 3, 4, 5, 6, 1, 2]
'''
回到上一篇文章統(tǒng)計(jì)選修數(shù)學(xué)的同學(xué)的平均分梳星。數(shù)據(jù)集如下
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":92
},
{
"name":"Music",
"score":80
}
]
},
]
求選修數(shù)學(xué)同學(xué)的平均分
studentmaths = From(students).map(lambda s: From(s["course"]).map(lambda c:
{
"name":s["name"],
"sex":s["sex"],
"course":c["name"],
"score":c["score"]
}).toList()).chain().filter(lambda it:it["course"]=="Math").toList()
#先打印出來(lái)看看
print(From(studentmaths).reduce(lambda acc,s:acc+s["score"],0)/len(studentmaths))
'''
結(jié)果:
[{'score': 90, 'name': 'wwb', 'sex': '1', 'course': 'Math'}, {'score': 92, 'name': 'wxb', 'sex': '1', 'course': 'Math'}]
91.0
'''
總結(jié)
高階函數(shù)和鏈?zhǔn)秸{(diào)用終于講完了,有沒(méi)有發(fā)現(xiàn)這些例子都好簡(jiǎn)潔滚朵,基本上多復(fù)雜的需求都只用一條鏈子冤灾,一直連擊。而且不失可讀性辕近。高階函數(shù)需要練習(xí)才會(huì)熟悉韵吨,像sql語(yǔ)句一樣,既簡(jiǎn)單又復(fù)雜移宅。預(yù)告下篇文章講組合归粉,就用我一年前寫(xiě)的玩具模板引擎為例子,200行左右吞杭。
測(cè)試代碼
同樣我自己把測(cè)試代碼貼出來(lái)吧盏浇,當(dāng)然需要和文章開(kāi)篇的FT.py放在一起才可以執(zhí)行。
from FT import From
print(From((1,2,3,4))
.filter(lambda it:it % 2==0)
.map(lambda it:it*2).toList())
print(From((1,2,3,4))
.filter(lambda it:it % 2==0)
.map(lambda it:it*2).reduce(lambda acc,it:acc+it))
print(From((1,2,3,4))
.filter(lambda it:it % 2==0)
.map(lambda it:it*2).reduce(lambda acc,it:acc+it,0))
testgroupdata=[{"id":1,"name":"wwb"},{"id":1,"name":"wxa"},{"id":1,"name":"wxb"},{"id":2,"name":"wxc"},{"id":2,"name":"wxd"}]
print(From(testgroupdata).groupby(lambda it:it['id']).toList())
print(From(testgroupdata).groupby(lambda it:it['id']).filter(lambda it:it[0]%2==0).toList())
#笛卡爾積
print(From((1,2,3)).product(('a','b','c')).toList())
students = [
{
"name":"wwb",
"book":[1,2,5]
},
{
"name":"wxa",
"book":[1,2,3]
},
{
"name":"wxb",
"book":[2,3,4]
}
]
books = [
{
"id":1,
"name":"C++ Primer"
},
{
"id":2,
"name":"Effecitve C++"
},
{
"id":3,
"name":"語(yǔ)文"
},{
"id":4,
"name":"數(shù)學(xué)"
},
{
"id":5,
"name":"英語(yǔ)"
}
]
print(From(students).map(lambda s:
{
"name":s["name"],
"book":From(s["book"]).product(books).filter(lambda it:it[0]==it[1]["id"])
.map(lambda it:it[1]["name"]).toList()
}).toList())
#只找一個(gè)
print(From(students).first(lambda it:it["name"]=="wwb"))
print(From(students).firstOrNone(lambda it:it["name"]=="wxx"))
#chain是一個(gè)很重要的函數(shù)
# 目的 ((a,b,c),(d,e,f),(g,h,i)) => (a,b,c,d,e,f,g,h,i),去掉一層括號(hào)
print(From(((1,2,3),(4,5,6),(1,2))).chain().toList())
#回到上次的例子
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":92
},
{
"name":"Music",
"score":80
}
]
},
]
#求選修數(shù)學(xué)同學(xué)的平均分
print(From(students).map(lambda s: From(s["course"]).map(lambda c:
{
"name":s["name"],
"sex":s["sex"],
"course":c["name"],
"score":c["score"]
}).toList()).chain().toList())
studentmaths = From(students).map(lambda s: From(s["course"]).map(lambda c:
{
"name":s["name"],
"sex":s["sex"],
"course":c["name"],
"score":c["score"]
}).toList()).chain().filter(lambda it:it["course"]=="Math").toList()
print(studentmaths)
print(From(studentmaths).reduce(lambda acc,s:acc+s["score"],0)/len(studentmaths))