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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容