Python學(xué)習(xí)筆記3-函數(shù)式編程

一.什么是函數(shù)式編程避诽?

函數(shù)式編程使用一系列的函數(shù)解決問題恼除。函數(shù)僅接受輸入并產(chǎn)生輸出兢仰,不包含任何能影響產(chǎn)生輸出的內(nèi)部狀態(tài)嬉愧。任何情況下贩挣,使用相同的參數(shù)調(diào)用函數(shù)始終能產(chǎn)生同樣的結(jié)果。
在一個函數(shù)式的程序中没酣,輸入的數(shù)據(jù)“流過”一系列的函數(shù)王财,每一個函數(shù)根據(jù)它的輸入產(chǎn)生輸出。函數(shù)式風(fēng)格避免編寫有“邊界效應(yīng)”(side effects)的函數(shù):修改內(nèi)部狀態(tài)裕便,或者是其他無法反應(yīng)在輸出上的變化佑稠。完全沒有邊界效應(yīng)的函數(shù)被稱為“純函數(shù)式的”(purely functional)朗若。避免邊界效應(yīng)意味著不使用在程序運行時可變的數(shù)據(jù)結(jié)構(gòu),輸出只依賴于輸入耻警。
可以認(rèn)為函數(shù)式編程剛好站在了面向?qū)ο缶幊痰膶α⒚婷誊浴ο笸ǔ0瑑?nèi)部狀態(tài)(字段)阱冶,和許多能修改這些狀態(tài)的函數(shù)顽冶,程序則由不斷修改狀態(tài)構(gòu)成采驻;函數(shù)式編程則極力避免狀態(tài)改動,并通過在函數(shù)間傳遞數(shù)據(jù)流進行工作视事。但這并不是說無法同時使用函數(shù)式編程和面向?qū)ο缶幊棠校聦嵣希瑥?fù)雜的系統(tǒng)一般會采用面向?qū)ο蠹夹g(shù)建模郑口,但混合使用函數(shù)式風(fēng)格還能讓你額外享受函數(shù)式風(fēng)格的優(yōu)點。
為什么使用函數(shù)式編程:
函數(shù)式的風(fēng)格通常被認(rèn)為有如下優(yōu)點:

  • 邏輯可證 這是一個學(xué)術(shù)上的優(yōu)點:沒有邊界效應(yīng)使得更容易從邏輯上證明程序是正確的(而不是通過測試)盾鳞。
  • 模塊化 函數(shù)式編程推崇簡單原則犬性,一個函數(shù)只做一件事情,將大的功能拆分成盡可能小的模塊腾仅。小的函數(shù)更易于閱讀和檢查錯誤乒裆。
  • 組件化 小的函數(shù)更容易加以組合形成新的功能。
    易于調(diào)試 細(xì)化的推励、定義清晰的函數(shù)使得調(diào)試更加簡單鹤耍。當(dāng)程序不正常運行時,每一個函數(shù)都是檢查數(shù)據(jù)是否正確的接口验辞,能更快速地排除沒有問題的代碼稿黄,定位到出現(xiàn)問題的地方。
  • 易于測試 不依賴于系統(tǒng)狀態(tài)的函數(shù)無須在測試前構(gòu)造測試樁跌造,使得編寫單元測試更加容易杆怕。
  • 更高的生產(chǎn)率 函數(shù)式編程產(chǎn)生的代碼比其他技術(shù)更少(往往是其他技術(shù)的一半左右)族购,并且更容易閱讀和維護。

下面依次介紹一些函數(shù)式編程的特性:

二.高階函數(shù)

高階函數(shù)英文叫Higher-order function陵珍。什么是高階函數(shù)寝杖?

  1. 變量可以指向函數(shù)
    abs是求絕對值的函數(shù),如下:
>>>f = abs
>>>f
<built-in function abs>

由上可見函數(shù)本身也可以賦值給變量,即:變量可以指向函數(shù)互纯。并且調(diào)用也是可以的

>>>f(-2))
2
  1. 函數(shù)名也是變量
    函數(shù)名是什么呢瑟幕?函數(shù)名其實就是指向函數(shù)的變量!對于abs()這個函數(shù)留潦,完全可以把函數(shù)名abs看成變量只盹,它指向一個可以計算絕對值的函數(shù)!
    如果把abs指向其他對象愤兵,會有什么情況發(fā)生鹿霸?
>>> abs = 10
>>> abs(-10)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'int' object is not callable

可以發(fā)現(xiàn)把abs指向10后,就無法通過abs(-10)調(diào)用該函數(shù)了秆乳!因為abs這個變量已經(jīng)不指向求絕對值函數(shù)而是指向一個整數(shù)10懦鼠!
當(dāng)然實際代碼絕對不能這么寫,這里是為了說明函數(shù)名也是變量屹堰。要恢復(fù)abs函數(shù)肛冶,需要重啟Python交互環(huán)境。

  1. 傳入函數(shù)
    既然變量可以指向函數(shù)扯键,函數(shù)的參數(shù)能接收變量睦袖,那么一個函數(shù)就可以接收另一個函數(shù)作為參數(shù),這種函數(shù)就稱之為高階函數(shù)荣刑。如下一個最簡單的高階函數(shù):
def add(x, y, f):
    return f(x) + f(y)

用代碼驗證一下:

>>> add(-5, 6, abs)
11

一些常見的高階函數(shù)

  1. map
    map函數(shù)是對一個序列的每個項依次執(zhí)行函數(shù)馅笙,下面是對一個序列每個項都乘以2:
>>>a=map(lambda x:x*x,[1,2,3])
>>>list(a)
[1, 4, 9]
  1. reduce
    reduce函數(shù)是對一個序列的每個項迭代調(diào)用函數(shù),下面是求3的階乘:
>>>from functools import reduce
>>>b=reduce (lambda x,y:x*y ,range(1,4))
>>>b
6

用reduce就可以用一行代碼寫出階乘函數(shù),如下:

>>>from functools import reduce
>>>g=lambda x: reduce (lambda a,b:a*b ,range(1,x+1))
>>>g(4)
24
  1. filter
    Python內(nèi)建的filter()函數(shù)用于過濾序列厉亏。
    和map()類似董习,filter()也接收一個函數(shù)和一個序列。和map()不同的是爱只,filter()把傳入的函數(shù)依次作用于每個元素皿淋,然后根據(jù)返回值是True還是False決定保留還是丟棄該元素
    例如,在一個list中恬试,刪掉偶數(shù)窝趣,只保留奇數(shù),可以這么寫:
def is_odd(n):
    return n % 2 == 1
list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
# 結(jié)果: [1, 5, 9, 15]

三.匿名函數(shù)

lambda是匿名函數(shù)训柴,沒有函數(shù)名哑舒,在閉包里和map reduce中很好用。
lambda提供了快速編寫簡單函數(shù)的能力畦粮。對于偶爾為之的行為散址,lambda讓你不再需要在編碼時跳轉(zhuǎn)到其他位置去編寫函數(shù)乖阵。 lambda表達式定義一個匿名的函數(shù),如果這個函數(shù)僅在編碼的位置使用到预麸,你可以現(xiàn)場定義瞪浸、直接使用
如下面是求兩數(shù)乘積的函數(shù):

lst.sort(lambda o1, o2: o1.compareTo(o2))

相信從這個小小的例子你也能感受到強大的生產(chǎn)效率

四.閉包

閉包就是函數(shù)中又定義函數(shù),里面的函數(shù)可以使用外部作用域內(nèi)的變量吏祸,但不是全局變量对蒲,所以一次創(chuàng)建外部函數(shù),則多次調(diào)用內(nèi)部函數(shù)時外部作用域內(nèi)的變量還是有效的贡翘;多次運行外部函數(shù)蹈矮,則會重新創(chuàng)建閉包,原來外部變量的值也不會受到影響鸣驱,如上例可以實現(xiàn)每次調(diào)用內(nèi)部函數(shù)是的外部變量+1操作泛鸟。這里說一下作用域的事兒,原來的python2只有全局作用域和局部作用域踊东,python3新增的nonlocal作用域就是專門針對閉包的北滥,nonlocal聲明的變量只能訪問外部作用域的變量,不能訪問全局作用域的變量闸翅,這樣就不用全局變量搞來搞去錯都不知道哪里出的了再芋。
如下所示:

def funA():
    x=0
    def funB():
        nonlocal x 
        x+=1
        print (x)
    return funB
a=funA()
a()
# 輸出1
a()
# 輸出2

如果再定義一個b=funA(),會得到一個新的funB,不會影響之前的閉包的值

a=funA()
a()
# 1
a()
# 2
b=funA()
b()
# 1
a()
# 3

可見,閉包在不影響可讀性的同時也省下了不少代碼量坚冀。

五.其他特性

  1. 避免使用變量
    為了避開邊界效應(yīng)济赎,函數(shù)式風(fēng)格盡量避免使用變量,而僅僅為了控制流程而定義的循環(huán)變量和流程中產(chǎn)生的臨時變量無疑是最需要避免的记某。 假如我們需要對剛才的數(shù)集進行過濾得到所有的正數(shù)司训,使用指令式風(fēng)格的代碼應(yīng)該像是這樣:
lst2 = list()
for i in range(len(lst)): #模擬經(jīng)典for循環(huán)
    if lst[i] > 0:
        lst2.append(lst[i])

這段代碼把從創(chuàng)建新列表、循環(huán)液南、取出元素豁遭、判斷、添加至新列表的整個流程完整的展示了出來贺拣,儼然把解釋器當(dāng)成了需要手把手指導(dǎo)的傻瓜。然而捂蕴,“過濾”這個動作是很常見的譬涡,為什么解釋器不能掌握過濾的流程,而我們只需要告訴它過濾規(guī)則呢啥辨? 在Python里涡匀,過濾由一個名為filter的內(nèi)置函數(shù)實現(xiàn)。有了這個函數(shù)溉知,解釋器就學(xué)會了如何“過濾”陨瘩,而我們只需要把規(guī)則告訴它:

lst2 = filter(lambda n: n > 0, lst)

這個函數(shù)帶來的好處不僅僅是少寫了幾行代碼這么簡單腕够。 封裝控制結(jié)構(gòu)后,代碼中就只需要描述功能而不是做法舌劳,這樣的代碼更清晰帚湘,更可讀。因為避開了控制結(jié)構(gòu)的干擾甚淡,第二段代碼顯然能讓你更容易了解它的意圖大诸。 另外,因為避開了索引贯卦,使得代碼中不太可能觸發(fā)下標(biāo)越界這種異常资柔,除非你手動制造一個。 函數(shù)式編程語言通常封裝了數(shù)個類似“過濾”這樣的常見動作作為模板函數(shù)撵割。唯一的缺點是這些函數(shù)需要少量的學(xué)習(xí)成本贿堰,但這絕對不能掩蓋使用它們帶來的好處。

  1. 內(nèi)置的不可變數(shù)據(jù)結(jié)構(gòu)
    為了避開邊界效應(yīng)啡彬,不可變的數(shù)據(jù)結(jié)構(gòu)是函數(shù)式編程中不可或缺的部分羹与。不可變的數(shù)據(jù)結(jié)構(gòu)保證數(shù)據(jù)的一致性,極大地降低了排查問題的難度外遇。 例如注簿,Python中的元組(tuple)就是不可變的,所有對元組的操作都不能改變元組的內(nèi)容跳仿,所有試圖修改元組內(nèi)容的操作都會產(chǎn)生一個異常诡渴。 函數(shù)式編程語言一般會提供數(shù)據(jù)結(jié)構(gòu)的兩種版本(可變和不可變),并推薦使用不可變的版本菲语。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末妄辩,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子山上,更是在濱河造成了極大的恐慌眼耀,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件佩憾,死亡現(xiàn)場離奇詭異哮伟,居然都是意外死亡,警方通過查閱死者的電腦和手機妄帘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進店門楞黄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人抡驼,你說我怎么就攤上這事鬼廓。” “怎么了致盟?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵碎税,是天一觀的道長尤慰。 經(jīng)常有香客問我,道長雷蹂,這世上最難降的妖魔是什么伟端? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮萎河,結(jié)果婚禮上荔泳,老公的妹妹穿的比我還像新娘。我一直安慰自己虐杯,他們只是感情好玛歌,可當(dāng)我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著擎椰,像睡著了一般支子。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上达舒,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天值朋,我揣著相機與錄音,去河邊找鬼巩搏。 笑死昨登,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的贯底。 我是一名探鬼主播丰辣,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼禽捆!你這毒婦竟也來了笙什?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤胚想,失蹤者是張志新(化名)和其女友劉穎琐凭,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體浊服,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡统屈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了牙躺。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鸿吆。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖述呐,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蕉毯,我是刑警寧澤乓搬,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布思犁,位于F島的核電站,受9級特大地震影響进肯,放射性物質(zhì)發(fā)生泄漏激蹲。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一江掩、第九天 我趴在偏房一處隱蔽的房頂上張望学辱。 院中可真熱鬧,春花似錦环形、人聲如沸策泣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽萨咕。三九已至,卻和暖如春火本,著一層夾襖步出監(jiān)牢的瞬間危队,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工钙畔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留茫陆,地道東北人。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓擎析,卻偏偏與公主長得像簿盅,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子叔锐,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,828評論 2 345

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