Python函數(shù)式介紹一 - 高階函數(shù)

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),""))

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市近哟,隨后出現(xiàn)的幾起案子驮审,更是在濱河造成了極大的恐慌,老刑警劉巖吉执,帶你破解...
    沈念sama閱讀 218,451評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件疯淫,死亡現(xiàn)場離奇詭異,居然都是意外死亡戳玫,警方通過查閱死者的電腦和手機(jī)熙掺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來咕宿,“玉大人币绩,你說我怎么就攤上這事蜡秽。” “怎么了缆镣?”我有些...
    開封第一講書人閱讀 164,782評論 0 354
  • 文/不壞的土叔 我叫張陵芽突,是天一觀的道長。 經(jīng)常有香客問我董瞻,道長寞蚌,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,709評論 1 294
  • 正文 為了忘掉前任钠糊,我火速辦了婚禮挟秤,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘眠蚂。我一直安慰自己煞聪,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,733評論 6 392
  • 文/花漫 我一把揭開白布逝慧。 她就那樣靜靜地躺著昔脯,像睡著了一般。 火紅的嫁衣襯著肌膚如雪笛臣。 梳的紋絲不亂的頭發(fā)上云稚,一...
    開封第一講書人閱讀 51,578評論 1 305
  • 那天,我揣著相機(jī)與錄音沈堡,去河邊找鬼静陈。 笑死,一個(gè)胖子當(dāng)著我的面吹牛诞丽,可吹牛的內(nèi)容都是我干的鲸拥。 我是一名探鬼主播,決...
    沈念sama閱讀 40,320評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼僧免,長吁一口氣:“原來是場噩夢啊……” “哼刑赶!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起懂衩,我...
    開封第一講書人閱讀 39,241評論 0 276
  • 序言:老撾萬榮一對情侶失蹤撞叨,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后浊洞,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體牵敷,經(jīng)...
    沈念sama閱讀 45,686評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,878評論 3 336
  • 正文 我和宋清朗相戀三年法希,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了枷餐。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,992評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡苫亦,死狀恐怖毛肋,靈堂內(nèi)的尸體忽然破棺而出奕锌,到底是詐尸還是另有隱情,我是刑警寧澤村生,帶...
    沈念sama閱讀 35,715評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站饼丘,受9級特大地震影響趁桃,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜肄鸽,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,336評論 3 330
  • 文/蒙蒙 一卫病、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧典徘,春花似錦蟀苛、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至梅鹦,卻和暖如春裆甩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背齐唆。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評論 1 270
  • 我被黑心中介騙來泰國打工嗤栓, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人箍邮。 一個(gè)月前我還...
    沈念sama閱讀 48,173評論 3 370
  • 正文 我出身青樓茉帅,卻偏偏與公主長得像,于是被迫代替她去往敵國和親锭弊。 傳聞我的和親對象是個(gè)殘疾皇子堪澎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,947評論 2 355

推薦閱讀更多精彩內(nèi)容