Python函數(shù)式編程

工作的項(xiàng)目主要是以Python進(jìn)行開(kāi)發(fā)的斥季,經(jīng)常會(huì)看到項(xiàng)目代碼中的lambda表達(dá)式還有一些把函數(shù)作為參數(shù)或放在集合中的用法。之前只是淺顯的知道是函數(shù)式編程纤壁,這里好好研究一下受楼。下面從以下幾個(gè)部分進(jìn)行學(xué)習(xí):

  • 什么是函數(shù)式編程?
  • 函數(shù)式編程的特點(diǎn)?
  • 函數(shù)式編程的用途?
  • Python中的函數(shù)式編程

什么是函數(shù)式編程?

簡(jiǎn)單說(shuō)银萍,"函數(shù)式編程"是一種"編程范式"(programming paradigm),也就是如何編寫(xiě)程序的方法論恤左。

它屬于"結(jié)構(gòu)化編程"的一種贴唇,主要思想是把運(yùn)算過(guò)程盡量寫(xiě)成一系列嵌套的函數(shù)調(diào)用。

函數(shù)式編程關(guān)心類(lèi)型(代數(shù)結(jié)構(gòu))之間的關(guān)系飞袋,是一種抽象程度很高的編程范式戳气;
而命令式編程則關(guān)心解決問(wèn)題的步驟。

函數(shù)式編程特點(diǎn)

函數(shù)式編程具有五個(gè)鮮明的特點(diǎn)巧鸭。

  1. 函數(shù)是"第一等公民"

所謂"第一等公民"(first class)瓶您,指的是函數(shù)與其他數(shù)據(jù)類(lèi)型一樣,處于平等地位纲仍,可以賦值給其他變量呀袱,也可以作為參數(shù),傳入另一個(gè)函數(shù)郑叠,或者作為別的函數(shù)的返回值夜赵。

舉例來(lái)說(shuō),下面代碼中的print變量就是一個(gè)函數(shù)乡革,可以作為另一個(gè)函數(shù)的參數(shù)寇僧。

var print = function(i){ console.log(i);};
[1,2,3].forEach(print);
  1. 只用"表達(dá)式"摊腋,不用"語(yǔ)句"

"表達(dá)式"(expression)是一個(gè)單純的運(yùn)算過(guò)程,總是有返回值嘁傀;"語(yǔ)句"(statement)是執(zhí)行某種操作兴蒸,沒(méi)有返回值。函數(shù)式編程要求细办,只使用表達(dá)式橙凳,不使用語(yǔ)句。也就是說(shuō)蟹腾,每一步都是單純的運(yùn)算痕惋,而且都有返回值区宇。

原因是函數(shù)式編程的開(kāi)發(fā)動(dòng)機(jī)娃殖,一開(kāi)始就是為了處理運(yùn)算(computation),不考慮系統(tǒng)的讀寫(xiě)(I/O)议谷。"語(yǔ)句"屬于對(duì)系統(tǒng)的讀寫(xiě)操作炉爆,所以就被排斥在外。

3. 沒(méi)有"副作用"

所謂"副作用"(side effect)卧晓,指的是函數(shù)內(nèi)部與外部互動(dòng)(最典型的情況芬首,就是修改全局變量的值),產(chǎn)生運(yùn)算以外的其他結(jié)果逼裆。

函數(shù)式編程強(qiáng)調(diào)沒(méi)有"副作用"郁稍,意味著函數(shù)要保持獨(dú)立,所有功能就是返回一個(gè)新的值胜宇,沒(méi)有其他行為耀怜,尤其是不得修改外部變量的值。

  1. 不修改狀態(tài)

上一點(diǎn)已經(jīng)提到桐愉,函數(shù)式編程只是返回新的值财破,不修改系統(tǒng)變量。因此从诲,不修改變量左痢,也是它的一個(gè)重要特點(diǎn)。

5. 引用透明

引用透明(Referential transparency)系洛,指的是函數(shù)的運(yùn)行不依賴(lài)于外部變量或"狀態(tài)"俊性,只依賴(lài)于輸入的參數(shù),任何時(shí)候只要參數(shù)相同描扯,引用函數(shù)所得到的返回值總是相同的磅废。

函數(shù)式編程的作用

  1. 代碼簡(jiǎn)潔,開(kāi)發(fā)快速
  2. 接近自然語(yǔ)言荆烈,易于理解
  3. 更方便的代碼管理
  4. 易于"并發(fā)編程"

Python中的函數(shù)式編程

在Python中拯勉,函數(shù)式編程主要通過(guò)使用:

  1. 高階函數(shù)
  2. 返回函數(shù)
  3. lambda函數(shù)
  4. 裝飾器
  5. 偏函數(shù)

高階函數(shù)

一個(gè)函數(shù)就可以接收另一個(gè)函數(shù)作為參數(shù)竟趾,這種函數(shù)就稱(chēng)之為高階函數(shù)。

例如宫峦,

def add(x, y, f):
    return f(x) + f(y)

print(add(-5, 6, abs))

Python中還內(nèi)置了三種常用的高階函數(shù):

  • map/reduce
>>> def f(x):
...     return x * x
...
>>> r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> list(r)
[1, 4, 9, 16, 25, 36, 49, 64, 81]

reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

>>> from functools import reduce
>>> def add(x, y):
...     return x + y
...
>>> reduce(add, [1, 3, 5, 7, 9])
25
  • filter
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]
  • sorted
>>> sorted([36, 5, -12, 9, -21], key=abs)
[5, 9, -12, -21, 36]

返回函數(shù)

返回求和函數(shù)

def lazy_sum(*args):
    def sum():
        ax = 0
        for n in args:
            ax = ax + n
        return ax
    return sum

閉包:一個(gè)函數(shù)定義中引用了函數(shù)外定義的變量岔帽,并且該函數(shù)可以在其定義環(huán)境外被執(zhí)行

def outer_func():
    loc_list = []
    def inner_func(name):
        loc_list.append(len(loc_list) + 1)
        print '%s loc_list = %s' %(name, loc_list)
    return inner_func

在這個(gè)例子中我們至少可以對(duì)閉包中引用的自由變量有如下的認(rèn)識(shí):

  • 閉包中的引用的自由變量只和具體的閉包有關(guān)聯(lián),閉包的每個(gè)實(shí)例引用的自由變量互不干擾导绷。
  • 一個(gè)閉包實(shí)例對(duì)其自由變量的修改會(huì)被傳遞到下一次該閉包實(shí)例的調(diào)用犀勒。

lambda函數(shù)

  1. 一個(gè)語(yǔ)法

在Python中,lambda的語(yǔ)法是唯一的妥曲。其形式如下:
lambda argument_list: expression

其中贾费,lambda是Python預(yù)留的關(guān)鍵字,argument_list和expression由用戶(hù)自定義檐盟。這里的argument_list是參數(shù)列表褂萧,expression是一個(gè)關(guān)于參數(shù)的表達(dá)式。
這里的lambda argument_list: expression表示的是一個(gè)函數(shù)葵萎。這個(gè)函數(shù)叫做lambda函數(shù)导犹。

  1. 三個(gè)特性

lambda函數(shù)有如下特性:

  • lambda函數(shù)是匿名的:所謂匿名函數(shù),通俗地說(shuō)就是沒(méi)有名字的函數(shù)羡忘。lambda函數(shù)沒(méi)有名字谎痢。
  • lambda函數(shù)有輸入和輸出:輸入是傳入到參數(shù)列表argument_list的值,輸出是根據(jù)表達(dá)式expression計(jì)算得到的值卷雕。
  • lambda函數(shù)一般功能簡(jiǎn)單:?jiǎn)涡衑xpression決定了lambda函數(shù)不可能完成復(fù)雜的邏輯节猿,只能完成非常簡(jiǎn)單的功能。由于其實(shí)現(xiàn)的功能一目了然漫雕,甚至不需要專(zhuān)門(mén)的名字來(lái)說(shuō)明滨嘱。

下面是一些lambda函數(shù)示例:

  • lambda x, y: xy;函數(shù)輸入是x和y蝎亚,輸出是它們的積xy
  • lambda:None九孩;函數(shù)沒(méi)有輸入?yún)?shù),輸出是None
  • lambda *args: sum(args); 輸入是任意個(gè)數(shù)的參數(shù)发框,輸出是它們的和(隱性要求是輸入?yún)?shù)必須能夠進(jìn)行加法運(yùn)算)
  • lambda **kwargs: 1躺彬;輸入是任意鍵值對(duì)參數(shù),輸出是1
  1. 四個(gè)用法

由于lambda語(yǔ)法是固定的梅惯,其本質(zhì)上只有一種用法宪拥,那就是定義一個(gè)lambda函數(shù)。在實(shí)際中铣减,根據(jù)這個(gè)lambda函數(shù)應(yīng)用場(chǎng)景的不同她君,可以將lambda函數(shù)的用法擴(kuò)展為以下幾種:

  • 將lambda函數(shù)賦值給一個(gè)變量,通過(guò)這個(gè)變量間接調(diào)用該lambda函數(shù)葫哗。
    例如缔刹,執(zhí)行語(yǔ)句add=lambda x, y: x+y球涛,定義了加法函數(shù)lambda x, y: x+y,并將其賦值給變量add校镐,這樣變量add便成為具有加法功能的函數(shù)亿扁。例如,執(zhí)行add(1,2)鸟廓,輸出為3从祝。
  • 將lambda函數(shù)賦值給其他函數(shù),從而將其他函數(shù)用該lambda函數(shù)替換引谜。
    例如牍陌,為了把標(biāo)準(zhǔn)庫(kù)time中的函數(shù)sleep的功能屏蔽(Mock),我們可以在程序初始化時(shí)調(diào)用:time.sleep=lambda x:None员咽。這樣毒涧,在后續(xù)代碼中調(diào)用time庫(kù)的sleep函數(shù)將不會(huì)執(zhí)行原有的功能。例如骏融,執(zhí)行time.sleep(3)時(shí)链嘀,程序不會(huì)休眠3秒鐘萌狂,而是什么都不做档玻。
  • 將lambda函數(shù)作為其他函數(shù)的返回值,返回給調(diào)用者茫藏。
    函數(shù)的返回值也可以是函數(shù)误趴。例如return lambda x, y: x+y返回一個(gè)加法函數(shù)。這時(shí)务傲,lambda函數(shù)實(shí)際上是定義在某個(gè)函數(shù)內(nèi)部的函數(shù)凉当,稱(chēng)之為嵌套函數(shù),或者內(nèi)部函數(shù)售葡。對(duì)應(yīng)的看杭,將包含嵌套函數(shù)的函數(shù)稱(chēng)之為外部函數(shù)。內(nèi)部函數(shù)能夠訪問(wèn)外部函數(shù)的局部變量挟伙,這個(gè)特性是閉包(Closure)編程的基礎(chǔ)楼雹,在這里我們不展開(kāi)。
  • 將lambda函數(shù)作為參數(shù)傳遞給其他函數(shù)尖阔。

例如:

dim_met_list = sorted(dim_met_list, key=lambda d: (int(d["map_type"]), 
int(d["dim_met_order"])))
list(map(lambda x: DATA_SET_PREFIX + x, DATA_SET_FIELD_SET))
  1. 一個(gè)爭(zhēng)議
  • 支持方認(rèn)為使用lambda編寫(xiě)的代碼更緊湊贮缅,更“pythonic”。
  • 反對(duì)方認(rèn)為介却,lambda函數(shù)能夠支持的功能十分有限谴供,其不支持多分支程序if...elif...else...和異常處理程序try ...except...。并且齿坷,lambda函數(shù)的功能被隱藏桂肌,對(duì)于編寫(xiě)代碼之外的人員來(lái)說(shuō)数焊,理解lambda代碼需要耗費(fèi)一定的理解成本。

裝飾器

一種常用的返回函數(shù)崎场,特殊的閉包

偏函數(shù)

>>> int('12345')
12345
>>> int('12345', base=8)
5349
>>> import functools
>>> int2 = functools.partial(int, base=2)
>>> int2('1000000')
64
>>> int2('1010101')
85

總結(jié)

個(gè)人理解在一些數(shù)值計(jì)算和裝飾器場(chǎng)景可以使用函數(shù)式編程使代碼更簡(jiǎn)單昌跌,但是復(fù)雜的業(yè)務(wù)場(chǎng)景不建議使用函數(shù)式編程,會(huì)使代碼難以理解照雁,增加維護(hù)成本蚕愤,降低開(kāi)發(fā)效率。

參考文章:
https://www.liaoxuefeng.com/wiki/1016959663602400/1017328525009056

http://www.ruanyifeng.com/blog/2012/04/functional_programming.html

https://zhuanlan.zhihu.com/p/67978661

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末饺蚊,一起剝皮案震驚了整個(gè)濱河市萍诱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌污呼,老刑警劉巖裕坊,帶你破解...
    沈念sama閱讀 211,639評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異燕酷,居然都是意外死亡籍凝,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)苗缩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)饵蒂,“玉大人,你說(shuō)我怎么就攤上這事酱讶⊥硕ⅲ” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,221評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵泻肯,是天一觀的道長(zhǎng)渊迁。 經(jīng)常有香客問(wèn)我,道長(zhǎng)灶挟,這世上最難降的妖魔是什么琉朽? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,474評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮稚铣,結(jié)果婚禮上箱叁,老公的妹妹穿的比我還像新娘。我一直安慰自己榛泛,他們只是感情好蝌蹂,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著曹锨,像睡著了一般孤个。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上沛简,一...
    開(kāi)封第一講書(shū)人閱讀 49,816評(píng)論 1 290
  • 那天齐鲤,我揣著相機(jī)與錄音斥废,去河邊找鬼。 笑死给郊,一個(gè)胖子當(dāng)著我的面吹牛牡肉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播淆九,決...
    沈念sama閱讀 38,957評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼统锤,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了炭庙?” 一聲冷哼從身側(cè)響起饲窿,我...
    開(kāi)封第一講書(shū)人閱讀 37,718評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎焕蹄,沒(méi)想到半個(gè)月后逾雄,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,176評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡腻脏,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評(píng)論 2 327
  • 正文 我和宋清朗相戀三年鸦泳,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片永品。...
    茶點(diǎn)故事閱讀 38,646評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡做鹰,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出腐碱,到底是詐尸還是另有隱情誊垢,我是刑警寧澤掉弛,帶...
    沈念sama閱讀 34,322評(píng)論 4 330
  • 正文 年R本政府宣布症见,位于F島的核電站,受9級(jí)特大地震影響殃饿,放射性物質(zhì)發(fā)生泄漏谋作。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評(píng)論 3 313
  • 文/蒙蒙 一乎芳、第九天 我趴在偏房一處隱蔽的房頂上張望遵蚜。 院中可真熱鬧,春花似錦奈惑、人聲如沸吭净。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,755評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)寂殉。三九已至,卻和暖如春原在,著一層夾襖步出監(jiān)牢的瞬間友扰,已是汗流浹背彤叉。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,987評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留村怪,地道東北人秽浇。 一個(gè)月前我還...
    沈念sama閱讀 46,358評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像甚负,于是被迫代替她去往敵國(guó)和親柬焕。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評(píng)論 2 348

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