10分鐘學(xué)會python函數(shù)式編程

在這篇文章里历筝,你將學(xué)會什么是函數(shù)范式以及如何使用Python進(jìn)行函數(shù)式編程厦瓢。你也將了解列表推導(dǎo)和其它形式的推導(dǎo)狂男。

函數(shù)范式

在命令式范式中敢靡,通過為計算機(jī)提供一系列指令然后執(zhí)行它們來完成任務(wù)立哑。在執(zhí)行這些指令時夜惭,可以改變某些狀態(tài)。例如铛绰,假設(shè)你最初將A設(shè)置為5诈茧,然后更改A的值。這時在變量內(nèi)部值的意義上捂掰,你改變了A的狀態(tài)敢会。

在函數(shù)式范式中,你不用告訴計算機(jī)做什么而是告訴他這個東西是什么这嚣。比如數(shù)字的最大公約數(shù)是什么鸥昏,從1到n的乘積是什么等等。

因此姐帚,變量不能變化吏垮。一旦你設(shè)置了一個變量,它就永遠(yuǎn)保持這種狀態(tài)(注意罐旗,在純函數(shù)式語言中膳汪,它們不是變量)。因此九秀,函數(shù)式編程沒有副作用遗嗽。副作用指的是函數(shù)改變它自己以外的東西。讓我們看一些典型Python代碼的示例:

這段代碼的輸出是5颤霎。在函數(shù)式范式中媳谁,改變變量是一個很大的禁忌涂滴,并且具有影響其范圍之外事物的功能也是一個很大的禁忌。函數(shù)唯一能做的就是計算一些東西并將其作為結(jié)果返回晴音。

現(xiàn)在你可能會想:“沒有變量柔纵,沒有副作用?為什么這樣好锤躁?“這個問題問得好搁料,我相信大多數(shù)人對此感到疑惑。

如果使用相同的參數(shù)調(diào)用函數(shù)兩次系羞,則保證返回相同的結(jié)果郭计。如果你已經(jīng)學(xué)習(xí)了數(shù)學(xué)函數(shù),你就會知道這個好處椒振。這稱為參照透明度昭伸。由于函數(shù)沒有副作用,如果你正在構(gòu)建一個計算某些事情的程序澎迎,你可以加速程序庐杨。如果每次調(diào)用func(2)都返回3,我們可以將它存儲在表中夹供,這可以防止程序重復(fù)運行相同的功能灵份。

通常,在函數(shù)式編程中哮洽,我們不使用循環(huán)填渠。我們使用遞歸。遞歸是一個數(shù)學(xué)概念鸟辅,通常意味著“自我調(diào)用”氛什。使用遞歸函數(shù),該函數(shù)將其自身作為子函數(shù)重復(fù)調(diào)用剔桨。這是Python中遞歸函數(shù)的一個很好的例子:

?在學(xué)習(xí)中有迷茫不知如何學(xué)習(xí)的朋友小編推薦一個學(xué)Python的學(xué)習(xí)q u n 227? -435-? 450可以來了解一起進(jìn)步一起學(xué)習(xí)屉更!

有些編程語言也具有惰性徙融。這意味著他們直到最后一秒才計算或做任何事情洒缀。如果你編寫一些代碼來執(zhí)行2 + 2,函數(shù)程序只會在你真正需要使用結(jié)果時計算出來欺冀。我們很快就會在Python中探索惰性树绩。

Map

為了理解,我們先來看看迭代是什么隐轩。通辰确梗可以迭代的對象是列表或數(shù)組,但Python有許多不同的類型可以迭代职车。你甚至可以創(chuàng)建自己的對象瘫俊,這些對象可以通過實現(xiàn)魔術(shù)方法進(jìn)行迭代鹊杖。魔術(shù)方法就像是一個API,可以幫助你的對象變得更加Pythonic扛芽。您需要實現(xiàn)2個魔術(shù)方法才能使對象成為可迭代的:

第一個魔術(shù)方法“__iter__”(注:這里是雙下劃線)返回迭代對象骂蓖,這通常在循環(huán)開始時使用〈猓”__next__“返回下一個對象登下。

讓我們快速進(jìn)入一個終端調(diào)用上面的代碼:

運行將會打印出

在Python中,迭代器是一個只有__iter__魔術(shù)方法的對象叮喳。這意味著您可以訪問對象中的位置被芳,但不能遍歷該對象。一些對象將具有魔術(shù)方法__next__而不是__iter__魔術(shù)方法馍悟,例如集合(在本文后面討論)畔濒。對于本文,我們假設(shè)我們接觸的所有內(nèi)容都是可迭代的對象锣咒。

現(xiàn)在我們知道什么是可迭代對象了篓冲,讓我們回到map函數(shù)。 map函數(shù)允許我們將函數(shù)應(yīng)用于iterable中的每一項宠哄。 Map需要2個輸入壹将,它們分別是要應(yīng)用的函數(shù)和可迭代對象。

假設(shè)我們有一個數(shù)字列表毛嫉,如下所示:

我們想要對每個數(shù)字進(jìn)行平方诽俯,我們可以編寫如下代碼:

Python中函數(shù)式的函數(shù)是具有惰性的。如果我們不使用“l(fā)ist”承粤,該函數(shù)將存儲iterable的定義暴区,而不是列表本身。我們需要明確告訴Python“把它變成一個列表”供我們使用辛臊。

在Python中突然從非惰性求值轉(zhuǎn)向惰性求值有點奇怪仙粱。如果你在函數(shù)式思維方式中考慮得更多,而不是命令式思維方式彻舰,那么你最終會習(xí)慣它伐割。

現(xiàn)在寫一個像“square(num)”這樣的普通函數(shù)雖然很好,但卻是不對的刃唤。我們必須定義一個完整的函數(shù)才能在map中使用它隔心?好吧,我們可以使用lambda(匿名)函數(shù)在map中定義一個函數(shù)尚胞。

Lambda表達(dá)式

lambda表達(dá)式是一個只有一行的函數(shù)硬霍。舉個例子,這個lambda表達(dá)式對給定的數(shù)字進(jìn)行平方:

讓我們運行它:

這看起來不像一個函數(shù)嗎笼裳?

嗯唯卖,這有點令人困惑粱玲,但可以解釋。我們將一些東西分配給變量“square”拜轨。那這個呢:

告訴Python這是一個lambda函數(shù)密幔,輸入叫做x。冒號之后的任何內(nèi)容都是您對輸入所做的操作撩轰,它會自動返回結(jié)果胯甩。

簡化我們的square程序到只有一行代碼,我們可以這樣做:

所以在lambda表達(dá)式中堪嫂,所有參數(shù)都在左邊偎箫,你要用它們做的東西在右邊。它有點亂皆串。但事實是淹办,編寫只有其他函數(shù)式程序員才能閱讀的代碼會有一定的樂趣。此外恶复,使用一個函數(shù)并將其轉(zhuǎn)換為一行代碼是非沉酷的。

Reduce

Reduce是一個將迭代變成一個東西的函數(shù)谤牡。通常副硅,你可以在列表上使用reduce函數(shù)執(zhí)行計算以將其減少到一個數(shù)字。 Reduce看起來像這樣:

我們經(jīng)常會使用lambda表達(dá)式作為函數(shù)翅萤。

列表的乘積是每個單獨的數(shù)字相乘恐疲。要做到這一點你將編寫如下代碼:

但是使用reduce你可以這樣寫:

獲得相同的功能,代碼更短套么,并且在使用函數(shù)式編程的情況下更整潔培己。(注:reduce函數(shù)在Python3中已不是內(nèi)置函數(shù),需要從functools模塊中導(dǎo)入)

Filter

filter函數(shù)采用可迭代的方式胚泌,并過濾掉你在該可迭代中不需要的所有內(nèi)容省咨。

通常,filter需要一個函數(shù)和一個列表玷室。它將函數(shù)應(yīng)用于列表中的每一項零蓉,如果該函數(shù)返回True,則不執(zhí)行任何操作阵苇。如果返回False壁公,則從列表中刪除該項。

語法如下:

讓我們看一個小例子绅项,沒有filter我們會寫:

使用filter,可以這樣寫:

高階函數(shù)

高階函數(shù)可以將函數(shù)作為參數(shù)并返回函數(shù)比肄。一個非常簡單的例子如下:

第二個返回函數(shù)的例子:

開頭我說過純函數(shù)式編程語言沒有變量快耿。更高階的函數(shù)使這變得更容易囊陡。

Python中的所有函數(shù)都是一等公民。一等公民被定義為具有以下一個或多個特征:

在運行時創(chuàng)建

在數(shù)據(jù)結(jié)構(gòu)中分配變量或元素

作為函數(shù)的參數(shù)傳遞

作為函數(shù)的結(jié)果返回

Python中的所有函數(shù)都可以用作高階函數(shù)掀亥。

Partial application

Partial application(也稱為閉包)有點奇怪撞反,但非常酷搪花。您可以在不提供所需的所有參數(shù)的情況下調(diào)用函數(shù)遏片。讓我們在一個例子中看到這一點。我們想要創(chuàng)建一個函數(shù)撮竿,它接受2個參數(shù)吮便,一個基數(shù)和一個指數(shù),并返回指數(shù)冪的基數(shù)幢踏,如下所示:

現(xiàn)在我們想要一個專用的平方函數(shù)髓需,使用冪函數(shù)計算出數(shù)字的平方:

這有效,但如果我們想要一個立方體功能呢房蝉?或者求四次方的功能呢贾铝?我們可以繼續(xù)寫下它們嗎刻蟹?好吧,你可以。但程序員很懶的蚀瘸。如果你一遍又一遍地重復(fù)同樣的事情,這表明有一種更快的方法來加快速度内狸,這將使你不再重復(fù)桥爽。我們可以在這里使用閉包。讓我們看一個使用閉包的square函數(shù)的示例:

是不是很酷续扔!我們可以只使用1個參數(shù)來調(diào)用需要2個參數(shù)的函數(shù)攻臀。

我們還可以使用一個循環(huán)來生成一個冪函數(shù),該函數(shù)實現(xiàn)從立方體一直到1000的冪纱昧。


函數(shù)式編程不是pythonic

您可能已經(jīng)注意到了刨啸,我們想要在函數(shù)式編程中做的很多事情都圍繞著列表。除了reduce函數(shù)和閉包之外识脆,您看到的所有函數(shù)都會生成列表设联。 Guido(Python之父)不喜歡Python中的函數(shù)式,因為Python已經(jīng)有了自己生成列表的方法灼捂。

如果你在Python的交互環(huán)境下寫入”import this“离例,你將會得到:

這是Python之禪。這是一首關(guān)于Pythonic意味著什么的詩悉稠。我們想要涉及的部分是:

There should be one — and preferably only one — obvious way to do it.(應(yīng)該盡量找到一種宫蛆,最好是唯一一種明顯的解決方案)

在Python中,map和filter可以執(zhí)行與列表推導(dǎo)(下面討論)相同的操作的猛。這打破了Python之禪的一個規(guī)則耀盗,因此函數(shù)式編程的這些部分不被視為“pythonic”想虎。

另一個話題是Lambda。在Python中叛拷,lambda函數(shù)是一個普通函數(shù)舌厨。 Lambda是語法糖。這兩種說法是等價的忿薇。

普通函數(shù)可以執(zhí)行l(wèi)ambda函數(shù)可以執(zhí)行的所有操作裙椭,但它不能以相反的方式工作。 lambda函數(shù)不能完成普通函數(shù)可以執(zhí)行的所有操作署浩。

這是一個簡短的論證揉燃,為什么函數(shù)式編程不能很好地適應(yīng)整個Python生態(tài)系統(tǒng)。你可能已經(jīng)注意到我之前提到了列表推導(dǎo)瑰抵,我們現(xiàn)在將討論它們你雌。

列表推導(dǎo)

前面,我提到過你可以用map或filter做的任何事情二汛,你可以用列表推導(dǎo)婿崭。列表推導(dǎo)是一種在Python中生成列表的方法。語法是:

讓我們對列表中的每個數(shù)字進(jìn)行平方肴颊,例如:

我們可以看到如何將函數(shù)應(yīng)用于列表中的每一項氓栈。我們?nèi)绾螒?yīng)用filter呢?看看前面的代碼:

我們可以將其轉(zhuǎn)換成一個列表推導(dǎo)婿着,像這樣:

列表支持if這樣的語句授瘦。您不再需要將一百萬個函數(shù)應(yīng)用于某些東西以獲得您想要的東西。事實上竟宋,如果你想嘗試生成某種列表提完,那么使用列表推導(dǎo)看起來會更清晰,更容易丘侠。如果我們想要將列表中每個0以下的數(shù)字平方怎么辦徒欣?有了lambda,map和filter你會寫:

這似乎很長很復(fù)雜蜗字。通過列表推導(dǎo)打肝,它只是:

列表推導(dǎo)僅適用于列表。map,filter適合任何可迭代的對象挪捕,那么這有什么用呢粗梭?你可以對你遇到的任何可迭代對象使用任何推導(dǎo)。

其他推導(dǎo)

你可以為任何可迭代對象創(chuàng)建一個推導(dǎo)级零。

可以使用推導(dǎo)生成任何可迭代的對象断医。從Python 2.7開始,您甚至可以生成字典(hashmap)。

如果它是可迭代的孩锡,則可以生成它酷宵。讓我們看一下最后一組的例子亥贸。

set是一個元素列表躬窜,在該列表中沒有元素重復(fù)兩次。

set中的元素沒有順序炕置。

您可能會注意到set(集合)與dict(字典)具有相同的花括號荣挨。 Python非常聰明。根據(jù)你是否為dict提供值朴摊,它會知道你是在寫dict推導(dǎo)還是set推導(dǎo)默垄。

總結(jié)

函數(shù)式編程美觀而純粹。函數(shù)式代碼可以很干凈甚纲,但也可能很亂口锭。一些Python程序員不喜歡Python中的函數(shù)式編程。但我認(rèn)為介杆,你應(yīng)該在解決問題時鹃操,使用最佳工具。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末春哨,一起剝皮案震驚了整個濱河市荆隘,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌赴背,老刑警劉巖椰拒,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異凰荚,居然都是意外死亡燃观,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進(jìn)店門便瑟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來缆毁,“玉大人,你說我怎么就攤上這事胳徽』” “怎么了?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵养盗,是天一觀的道長缚陷。 經(jīng)常有香客問我,道長往核,這世上最難降的妖魔是什么箫爷? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上虎锚,老公的妹妹穿的比我還像新娘硫痰。我一直安慰自己,他們只是感情好窜护,可當(dāng)我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布效斑。 她就那樣靜靜地躺著,像睡著了一般柱徙。 火紅的嫁衣襯著肌膚如雪缓屠。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天护侮,我揣著相機(jī)與錄音敌完,去河邊找鬼。 笑死羊初,一個胖子當(dāng)著我的面吹牛滨溉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播长赞,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼晦攒,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了涧卵?” 一聲冷哼從身側(cè)響起勤家,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎柳恐,沒想到半個月后伐脖,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡乐设,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年讼庇,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片近尚。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡蠕啄,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出戈锻,到底是詐尸還是另有隱情歼跟,我是刑警寧澤,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布格遭,位于F島的核電站哈街,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏拒迅。R本人自食惡果不足惜骚秦,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一她倘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧作箍,春花似錦硬梁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至懒震,卻和暖如春罩息,著一層夾襖步出監(jiān)牢的瞬間嗤详,已是汗流浹背个扰。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留葱色,地道東北人递宅。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像苍狰,于是被迫代替她去往敵國和親办龄。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,779評論 2 354

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