python--------函數(shù)式編程【匿名 笤喳、高級、map碌宴、filter杀狡、reduce、zip贰镣、sorted函數(shù)】

@[toc]

函數(shù)式編程概述

  • 函數(shù)式編程: 函數(shù)式=編程語言定義的函數(shù)+數(shù)學(xué)意義的函數(shù)

優(yōu)點:

  • 便于進(jìn)行單元測試
  • 便于調(diào)試
  • 適合并行執(zhí)行

特性

  • 不可變數(shù)據(jù)(不可變:不用變量保存狀態(tài)呜象,不修改變量)

非函數(shù)式:

a=1
def incr_test1():
    global a
    a+=1
    return a
print(incr_test1())

運(yùn)行結(jié)果

2

函數(shù)式:

n=1
def incr_test2(n):
    return n+1
print(incr_test2(2))

運(yùn)行結(jié)果

3
  • 第一類對象(函數(shù)即"變量")---------看下面高階函數(shù)的條件
  • 尾調(diào)用優(yōu)化(尾遞歸) 尾調(diào)用:在函數(shù)最后一步調(diào)用另外一個函數(shù)(最后一行不一定是函數(shù)的最后一步)

函數(shù)bar3在foo3內(nèi)為尾調(diào)用

def bar3(n):
    return n
def foo3():
    return bar3(3)

函數(shù)bar4和bar5在foo4內(nèi)均為尾調(diào)用膳凝,二者在if判斷條件不同的情況下都有可能作為函數(shù)的最后一步

def bar4(n):
    return n
def bar5(n):
    return n+1
def foo4(x):
    if type(x) is str:
        return bar4(x)
    elif type(x) is int:
        return bar5(x)

函數(shù)bar6在foo5內(nèi)為非尾調(diào)用

def bar6(n):
    return n
def foo5(x):
    y=bar6(x)
    return y

函數(shù)bar7在foo6內(nèi)非尾調(diào)用

def bar7(n):
    return n
def foo6(x):
    return bar7(x)+1

高階函數(shù)

下面兩個條件滿足其中一個即可:

  • 函數(shù)可以接收另一個函數(shù)作為參數(shù)
def add(x,y,f):
    return f(x)+f(y)
print(add(-5,6,abs)) #f=abs abs()求絕對值

運(yùn)行結(jié)果

11
  • 返回值可以是函數(shù)名
def bar1():
    print('from bar1')
def foo1():
    print('from foo')
    return bar1
m=foo1() #n=bar1
m()

運(yùn)行結(jié)果

from foo
from bar1
def hanle():
    print('from hanle')
    return hanle
h=hanle()
h()

運(yùn)行結(jié)果

from hanle
from hanle

這個就不是高階函數(shù)

def foo(n):
    print(n)
def bar(name):
    print('i am name is %s'%name)
foo(bar('alex')) #bar函數(shù)沒有返回值,返回值不是函數(shù)名

運(yùn)行結(jié)果

i am name is alex
None

匿名函數(shù)

lambda 參數(shù)列表:函數(shù)返回值表達(dá)式語句

  • 關(guān)鍵字lambda表示匿名函數(shù)
  • 冒號前表示參數(shù)恭陡,冒號后表示返回值
def func(x):
    return x+1
res=func(10)
print(res)

#相當(dāng)于
res=lambda x:x+1
print(res(10))   #記得帶參數(shù)

運(yùn)行結(jié)果

11
11
res=lambda x:x+'_ab'
print(res('alex'))

運(yùn)行結(jié)果

alex_ab

lambda表達(dá)式序列

  • 可以將lambda表達(dá)式作為序列(如列表蹬音、元組或字典等)元素,從而實現(xiàn)挑戰(zhàn)表的功能休玩,也就是函數(shù)的列表

序列=[(lambda 表達(dá)式1),(lambda 表達(dá)式2), ...]

調(diào)用序列中l(wèi)ambda表達(dá)式的方法

序列[索引](lambda 表達(dá)式的參數(shù)列表)

定義一個lambda表達(dá)式序列著淆,第一個元素用于計算參數(shù)的平方,第二個元素用于計算參數(shù)的立方拴疤,第三個元素用于計算參數(shù)的四次方

arr=[(lambda x:x**2),(lambda x:x**3),(lambda x:x**4)]
print(arr[0](2),arr[1](2),arr[2](2))

運(yùn)行結(jié)果

4 8 16

將lambda表達(dá)式作為函數(shù)的返回值

def math(o):
    if(o==1):
        return lambda x,y:x+y
    if(o==2):
        return lambda x,y:x-y
    if(o==3):
        return lambda x,y:x*y
    if(o==4):
        return lambda x,y:x/y
action=math(1) #返回加法lambda表達(dá)式
print('10+2=',action(10,2))
action=math(2) #返回減法lambda表達(dá)式
print('10-2=',action(10,2))
action=math(3) #返回乘法lambda表達(dá)式
print('10*2=',action(10,2))
action=math(4) #返回除法lambda表達(dá)式
print('10/2=',action(10,2))

運(yùn)行結(jié)果

10+2= 12
10-2= 8
10*2= 20
10/2= 5.0

python函數(shù)式編程常用的函數(shù)(它們也是高階函數(shù))

map()函數(shù)

  • map()函數(shù)用于指定序列中的所有元素作為參數(shù)調(diào)用指定函數(shù)永部,并將結(jié)果構(gòu)成一個新的序列返回
  • map()函數(shù)接收兩個參數(shù),一個是函數(shù)呐矾,一個是Iterable(可迭代對象)苔埋,map將傳入的函數(shù)依次作用到序列的每個元素,并把結(jié)果作為新的Iterator返回
  • map()作為高階函數(shù)凫佛,事實上它把運(yùn)算規(guī)則抽象了
arr=map(lambda x:x**2,[2,4,6,8,10])
for n in arr:
    print(n)

運(yùn)行結(jié)果

4
16
36
64
100
#enumerate() 函數(shù)用于將一個可遍歷的數(shù)據(jù)對象(如列表讲坎、元組或字符串)組合為一個索引序列,同時列出數(shù)據(jù)和數(shù)據(jù)下標(biāo)愧薛,一般用在 for 循環(huán)當(dāng)中
arr=map(lambda x:x**2,[2,4,6,8,10])
for i in enumerate(arr):
    print(i)

arr=map(lambda x,y:x+y,[1,2,3,4],[5,6,7,8])  #兩個序列
for m in enumerate(arr):
    print(m)

運(yùn)行結(jié)果

(0, 4)
(1, 16)
(2, 36)
(3, 64)
(4, 100)


(0, 6)
(1, 8)
(2, 10)
(3, 12)
print(map(str,[1,2,3,4,5,6]))
print(list(map(str,[1,2,3,4,5,6])))  #轉(zhuǎn)換成字符串

運(yùn)行結(jié)果

<map object at 0x037345D0>   #內(nèi)存地址
['1', '2', '3', '4', '5', '6']

reduce()函數(shù)

  • reduce把一個函數(shù)作用在一個序列上晨炕,這個函數(shù)必須接收兩個參數(shù),reduce把結(jié)果繼續(xù)和序列的下一個元素做累積計算

==從python3.0后毫炉,reduce()函數(shù)不被集成在python內(nèi)置函數(shù)中瓮栗,需要使用下面的語句引用functools模塊,才能調(diào)用reduce()函數(shù)==

    from functools import reduce
def func(x,y):
    return x+y
sum=reduce(func,(2,4,6,8,10))
print(sum)

運(yùn)行結(jié)果

30

程序運(yùn)算過程

  • (1)reduce()函數(shù)首先使用2和4參數(shù)調(diào)用func()函數(shù)瞄勾,得到結(jié)果6
  • (2)使用結(jié)果6和序列第三個元素6為參數(shù)調(diào)用func()函數(shù)费奸,得到結(jié)果12
  • (3)使用結(jié)果12和序列第四個元素8為參數(shù)調(diào)用func()函數(shù),得到結(jié)果20
  • (4)使用結(jié)果20和序列的第五個元素10為參數(shù)調(diào)用func()函數(shù)进陡,得到結(jié)果30

如果要把序列[1, 3, 5, 7, 9]變換成整數(shù)13579愿阐,reduce就可以派上用場

from functools import reduce
def func1(x,y):
    return x*10+y
a=reduce(func1,[1,3,5,7,9])
print(a)

運(yùn)行結(jié)果

13579

filter()函數(shù)

和map()類似,filter()也接收一個函數(shù)和一個序列趾疚。和map()不同的是缨历,filter()把傳入的函數(shù)依次作用于每個元素,然后根據(jù)返回值是True還是False決定保留還是丟棄該元素糙麦。

在一個list中辛孵,刪掉偶數(shù),只保留奇數(shù)

def is_odd(n):
    return n%2==1
print(list(filter(is_odd,[1,2,3,4,5,6,9,10,15])))

運(yùn)行結(jié)果

[1, 3, 5, 9, 15]

把一個序列中的空字符串刪掉

def not_empty(s):
    return s and s.strip()
print(list(filter(not_empty,['a',' ','b',None,'c',' '])))

運(yùn)行結(jié)果

['a', 'b', 'c']
people=[{'name':'alex','age':100000},
        {'name':'xiaoli','age':10000000},
        {'name':'xiaoguo','age':9000},
        {'name':'xiaowang','age':18}
        ]
print(list(filter(lambda p:p['age']<=18,people)))

運(yùn)行結(jié)果

[{'name': 'xiaowang', 'age': 18}]
  • 可見用filter()這個高階函數(shù)赡磅,關(guān)鍵在于正確實現(xiàn)一個“篩選”函數(shù)
  • 注意到filter()函數(shù)返回的是一個Iterator魄缚,也就是一個惰性序列,所以要強(qiáng)迫filter()完成計算結(jié)果焚廊,需要用list()函數(shù)獲得所有結(jié)果并返回list

map() filter() reduce() 區(qū)別

  • map() 處理序列中的每個元素冶匹,得到的結(jié)果是一個'列表'习劫,該'列表'元素個數(shù)及位置與原來一樣
  • filter()遍歷序列中的每一個元素,判斷每個元素得到的布爾值徙硅,如果是True則留下來
  • reduce() 處理一個序列榜聂,把序列進(jìn)行合并操作

sorted()函數(shù)

排序函數(shù),排序的核心是比較兩個元素的大小嗓蘑,如果是數(shù)字我們可以直接比較须肆,但如果是字符串或者是字典,直接比較數(shù)學(xué)上的大小是沒有意義的桩皿,因此豌汇,比較過程必須通過函數(shù)抽象出來

print(sorted([36,5,-12,9,-21]))
#sorted()函數(shù)也是一個高階函數(shù),它還可以接收一個key函數(shù)來實現(xiàn)自定義的排序泄隔,例如按絕對值大小排序
print(sorted([36,5,-12,9,-21],key=abs))

print(sorted(['bob','about','Zoo','Credit']))
#默認(rèn)情況下拒贱,對字符串排序,是按照ASCII的大小比較的佛嬉,由于'Z' < 'a'逻澳,結(jié)果,大寫字母Z會排在小寫字母a的前面暖呕。

#現(xiàn)在斜做,我們提出排序應(yīng)該忽略大小寫,按照字母序排序湾揽。要實現(xiàn)這個算法瓤逼,不必對現(xiàn)有代碼大加改動,只要我們能用一個key函數(shù)把字符串映射為忽略大小寫排序即可库物。忽略大小寫來比較兩個字符串霸旗,實際上就是先把字符串都變成大寫(或者都變成小寫),再比較
print(sorted(['bob','about','Zoo','Credit'],key=str.lower))

#反向排序
print(sorted(['bob','about','Zoo','Credit'],key=str.lower,reverse=True))

運(yùn)行結(jié)果

[-21, -12, 5, 9, 36]
[5, 9, -12, -21, 36]
['Credit', 'Zoo', 'about', 'bob']
['about', 'bob', 'Credit', 'Zoo']
['Zoo', 'Credit', 'bob', 'about']
L=[('Bob',75),('Adam',92),('Bart',66),('Lisa',88)]
def by_name(t):
    return t[0]
def by_score(t):
    return t[1]
L2=sorted(L,key=by_name)
print('根據(jù)名字排序:',L2)
L2=sorted(L,key=by_score,reverse=True)
print('根據(jù)成績排序:',L2)

運(yùn)行結(jié)果

根據(jù)名字排序: [('Adam', 92), ('Bart', 66), ('Bob', 75), ('Lisa', 88)]
根據(jù)成績排序: [('Adam', 92), ('Lisa', 88), ('Bob', 75), ('Bart', 66)]

zip()函數(shù)

zip()函數(shù)以一系列列表作為參數(shù)戚揭,將列表中對應(yīng)的元素打包成一個個元組诱告,然后返回由這些元組組成的列表

a=[1,2,3]
b=[4,5,6]
zipped=zip(a,b)
for i in zipped:
    print(i)

運(yùn)行結(jié)果

(1, 4)
(2, 5)
(3, 6)

如果傳入的參數(shù)長度不等,則返回列表的長度和參數(shù)中長度最短的列表相同

a=[1,2,3]
b=[4,5,6,7]
zipped=zip(a,b)
for n in zipped:
    print(n)

運(yùn)行結(jié)果

(1, 4)
(2, 5)
(3, 6)

將打包結(jié)果前面加上操作符民晒,并以此為參數(shù)調(diào)用zip()函數(shù)精居,可以將打包結(jié)果解壓*

a=[1,2,3]
b=[4,5,6]
zipped=zip(a,b)
unzipped=zip(*zipped)
for m in unzipped:
    print(m)

運(yùn)行結(jié)果

(1, 2, 3)
(4, 5, 6)

普通函數(shù)方式與函數(shù)式編程的對比

通過學(xué)習(xí)以上的函數(shù)可以更好的看出它們的區(qū)別

以普通編程方式計算列表元素中的正數(shù)之和

list=[2,-6,11,-7,8,15,-14,-1,10,-13,18]
sum=0
for i in range(len(list)):
    if list[i]>0:
        sum+=list[i]
print(sum)

運(yùn)行結(jié)果

64

以函數(shù)式編程方式實現(xiàn)

from functools import reduce
list=[2,-6,11,-7,8,15,-14,-1,10,-13,18]
s=filter(lambda x:x>0,list)
sum=reduce(lambda x,y:x+y,s)
print(sum)

運(yùn)行結(jié)果

64

相比而言,函數(shù)式編程具有如下幾個特點

  • 代碼更簡單
  • 數(shù)據(jù)镀虐、操作箱蟆、返回值都在一起
  • 沒有循環(huán)體沟绪,幾乎沒有臨時變量
  • 代碼用來描述要做什么刮便,而不是怎么去做
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市绽慈,隨后出現(xiàn)的幾起案子恨旱,更是在濱河造成了極大的恐慌辈毯,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,546評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件搜贤,死亡現(xiàn)場離奇詭異谆沃,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)仪芒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評論 3 395
  • 文/潘曉璐 我一進(jìn)店門唁影,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人掂名,你說我怎么就攤上這事据沈。” “怎么了饺蔑?”我有些...
    開封第一講書人閱讀 164,911評論 0 354
  • 文/不壞的土叔 我叫張陵锌介,是天一觀的道長。 經(jīng)常有香客問我猾警,道長孔祸,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,737評論 1 294
  • 正文 為了忘掉前任发皿,我火速辦了婚禮崔慧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘雳窟。我一直安慰自己尊浪,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,753評論 6 392
  • 文/花漫 我一把揭開白布封救。 她就那樣靜靜地躺著拇涤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪誉结。 梳的紋絲不亂的頭發(fā)上鹅士,一...
    開封第一講書人閱讀 51,598評論 1 305
  • 那天,我揣著相機(jī)與錄音惩坑,去河邊找鬼掉盅。 笑死,一個胖子當(dāng)著我的面吹牛以舒,可吹牛的內(nèi)容都是我干的趾痘。 我是一名探鬼主播,決...
    沈念sama閱讀 40,338評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蔓钟,長吁一口氣:“原來是場噩夢啊……” “哼永票!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起组力,我...
    開封第一講書人閱讀 39,249評論 0 276
  • 序言:老撾萬榮一對情侶失蹤敷燎,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后搪柑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體世分,經(jīng)...
    沈念sama閱讀 45,696評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡编振,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,888評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了臭埋。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片踪央。...
    茶點故事閱讀 40,013評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖瓢阴,靈堂內(nèi)的尸體忽然破棺而出杯瞻,到底是詐尸還是另有隱情,我是刑警寧澤炫掐,帶...
    沈念sama閱讀 35,731評論 5 346
  • 正文 年R本政府宣布魁莉,位于F島的核電站,受9級特大地震影響募胃,放射性物質(zhì)發(fā)生泄漏旗唁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,348評論 3 330
  • 文/蒙蒙 一痹束、第九天 我趴在偏房一處隱蔽的房頂上張望检疫。 院中可真熱鬧,春花似錦祷嘶、人聲如沸屎媳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽烛谊。三九已至,卻和暖如春嘉汰,著一層夾襖步出監(jiān)牢的瞬間丹禀,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評論 1 270
  • 我被黑心中介騙來泰國打工鞋怀, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留双泪,地道東北人。 一個月前我還...
    沈念sama閱讀 48,203評論 3 370
  • 正文 我出身青樓密似,卻偏偏與公主長得像焙矛,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子残腌,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,960評論 2 355

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