Python函數(shù)式介紹二 - 鏈?zhǔn)秸{(diào)用

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))
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末芽狗,一起剝皮案震驚了整個(gè)濱河市绢掰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖滴劲,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件攻晒,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡班挖,警方通過(guò)查閱死者的電腦和手機(jī)鲁捏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)萧芙,“玉大人给梅,你說(shuō)我怎么就攤上這事∷荆” “怎么了动羽?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)渔期。 經(jīng)常有香客問(wèn)我运吓,道長(zhǎng),這世上最難降的妖魔是什么疯趟? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任拘哨,我火速辦了婚禮,結(jié)果婚禮上信峻,老公的妹妹穿的比我還像新娘倦青。我一直安慰自己,他們只是感情好站欺,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布姨夹。 她就那樣靜靜地躺著,像睡著了一般矾策。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上峭沦,一...
    開(kāi)封第一講書(shū)人閱讀 51,679評(píng)論 1 305
  • 那天贾虽,我揣著相機(jī)與錄音,去河邊找鬼吼鱼。 笑死蓬豁,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的菇肃。 我是一名探鬼主播地粪,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼琐谤!你這毒婦竟也來(lái)了蟆技?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎质礼,沒(méi)想到半個(gè)月后旺聚,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡眶蕉,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年砰粹,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片造挽。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡碱璃,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出饭入,到底是詐尸還是另有隱情嵌器,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布圣拄,位于F島的核電站嘴秸,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏庇谆。R本人自食惡果不足惜岳掐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望饭耳。 院中可真熱鬧串述,春花似錦、人聲如沸寞肖。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)新蟆。三九已至觅赊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間琼稻,已是汗流浹背吮螺。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留帕翻,地道東北人鸠补。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像嘀掸,于是被迫代替她去往敵國(guó)和親紫岩。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

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