一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

腳本之家,腳本語言編程技術(shù)及教程分享平臺(tái)!
分類導(dǎo)航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服務(wù)器之家 - 腳本之家 - Python - Python函數(shù)式編程指南(二):從函數(shù)開始

Python函數(shù)式編程指南(二):從函數(shù)開始

2020-07-17 10:41Python教程網(wǎng) Python

這篇文章主要介紹了Python函數(shù)式編程指南(二):從函數(shù)開始,本文講解了定義一個(gè)函數(shù)、使用函數(shù)賦值、閉包、作為參數(shù)等內(nèi)容,需要的朋友可以參考下

2. 從函數(shù)開始
2.1. 定義一個(gè)函數(shù)
如下定義了一個(gè)求和函數(shù):

 

復(fù)制代碼 代碼如下:

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

 

關(guān)于參數(shù)和返回值的語法細(xì)節(jié)可以參考其他文檔,這里就略過了。

使用lambda可以定義簡(jiǎn)單的單行匿名函數(shù)。lambda的語法是:

 

復(fù)制代碼 代碼如下:

lambda args: expression


參數(shù)(args)的語法與普通函數(shù)一樣,同時(shí)表達(dá)式(expression)的值就是匿名函數(shù)調(diào)用的返回值;而lambda表達(dá)式返回這個(gè)匿名函數(shù)。如果我們給匿名函數(shù)取個(gè)名字,就像這樣:

復(fù)制代碼 代碼如下:

lambda_add = lambda x, y: x + y


這與使用def定義的求和函數(shù)完全一樣,可以使用lambda_add作為函數(shù)名進(jìn)行調(diào)用。然而,提供lambda的目的是為了編寫偶爾為之的、簡(jiǎn)單的、可預(yù)見不會(huì)被修改的匿名函數(shù)。這種風(fēng)格雖然看起來很酷,但并不是一個(gè)好主意,特別是當(dāng)某一天需要對(duì)它進(jìn)行擴(kuò)充,再也無法用一個(gè)表達(dá)式寫完時(shí)。如果一開始就需要給函數(shù)命名,應(yīng)該始終使用def關(guān)鍵字。

 

2.2. 使用函數(shù)賦值

事實(shí)上你已經(jīng)見過了,上一節(jié)中我們將lambda表達(dá)式賦值給了add。同樣,使用def定義的函數(shù)也可以賦值,相當(dāng)于為函數(shù)取了一個(gè)別名,并且可以使用這個(gè)別名調(diào)用函數(shù):

復(fù)制代碼 代碼如下:

add_a_number_to_another_one_by_using_plus_operator = add
print add_a_number_to_another_one_by_using_plus_operator(1, 2)


既然函數(shù)可以被變量引用,那么將函數(shù)作為參數(shù)和返回值就是很尋常的做法了。

 

2.3. 閉包
閉包是一類特殊的函數(shù)。如果一個(gè)函數(shù)定義在另一個(gè)函數(shù)的作用域中,并且函數(shù)中引用了外部函數(shù)的局部變量,那么這個(gè)函數(shù)就是一個(gè)閉包。下面的代碼定義了一個(gè)閉包:

復(fù)制代碼 代碼如下:

def f():
    n = 1
    def inner():
        print n
    inner()
    n = 'x'
    inner()


函數(shù)inner定義在f的作用域中,并且在inner中使用了f中的局部變量n,這就構(gòu)成了一個(gè)閉包。閉包綁定了外部的變量,所以調(diào)用函數(shù)f的結(jié)果是打印1和'x'。這類似于普通的模塊函數(shù)和模塊中定義的全局變量的關(guān)系:修改外部變量能影響內(nèi)部作用域中的值,而在內(nèi)部作用域中定義同名變量則將遮蔽(隱藏)外部變量。

 

如果需要在函數(shù)中修改全局變量,可以使用關(guān)鍵字global修飾變量名。Python 2.x中沒有關(guān)鍵字為在閉包中修改外部變量提供支持,在3.x中,關(guān)鍵字nonlocal可以做到這一點(diǎn):

復(fù)制代碼 代碼如下:

#Python 3.x supports `nonlocal'
def f():
    n = 1
    def inner():
        nonlocal n
        n = 'x'
    print(n)
    inner()
    print(n)

 

調(diào)用這個(gè)函數(shù)的結(jié)果是打印1和'x',如果你有一個(gè)Python 3.x的解釋器,可以試著運(yùn)行一下。

由于使用了函數(shù)體外定義的變量,看起來閉包似乎違反了函數(shù)式風(fēng)格的規(guī)則即不依賴外部狀態(tài)。但是由于閉包綁定的是外部函數(shù)的局部變量,而一旦離開外部函數(shù)作用域,這些局部變量將無法再從外部訪問;另外閉包還有一個(gè)重要的特性,每次執(zhí)行至閉包定義處時(shí)都會(huì)構(gòu)造一個(gè)新的閉包,這個(gè)特性使得舊的閉包綁定的變量不會(huì)隨第二次調(diào)用外部函數(shù)而更改。所以閉包實(shí)際上不會(huì)被外部狀態(tài)影響,完全符合函數(shù)式風(fēng)格的要求。(這里有一個(gè)特例,Python 3.x中,如果同一個(gè)作用域中定義了兩個(gè)閉包,由于可以修改外部變量,他們可以相互影響。)

雖然閉包只有在作為參數(shù)和返回值時(shí)才能發(fā)揮它的真正威力,但閉包的支持仍然大大提升了生產(chǎn)率。

2.4. 作為參數(shù)
如果你對(duì)OOP的模板方法模式很熟悉,相信你能很快速地學(xué)會(huì)將函數(shù)當(dāng)作參數(shù)傳遞。兩者大體是一致的,只是在這里,我們傳遞的是函數(shù)本身而不再是實(shí)現(xiàn)了某個(gè)接口的對(duì)象。
我們先來給前面定義的求和函數(shù)add熱熱身:

 

復(fù)制代碼 代碼如下:

print add('三角形的樹', '北極')

 

與加法運(yùn)算符不同,你一定很驚訝于答案是'三角函數(shù)'。這是一個(gè)內(nèi)置的彩蛋...bazinga!

言歸正傳。我們的客戶有一個(gè)從0到4的列表:

 

復(fù)制代碼 代碼如下:

lst = range(5) #[0, 1, 2, 3, 4]


雖然我們?cè)谏弦恍」?jié)里給了他一個(gè)加法器,但現(xiàn)在他仍然在為如何計(jì)算這個(gè)列表所有元素的和而苦惱。當(dāng)然,對(duì)我們而言這個(gè)任務(wù)輕松極了:

復(fù)制代碼 代碼如下:

amount = 0
for num in lst:
    amount = add(amount, num)


這是一段典型的指令式風(fēng)格的代碼,一點(diǎn)問題都沒有,肯定可以得到正確的結(jié)果。現(xiàn)在,讓我們?cè)囍煤瘮?shù)式的風(fēng)格重構(gòu)一下。

 

首先可以預(yù)見的是求和這個(gè)動(dòng)作是非常常見的,如果我們把這個(gè)動(dòng)作抽象成一個(gè)單獨(dú)的函數(shù),以后需要對(duì)另一個(gè)列表求和時(shí),就不必再寫一遍這個(gè)套路了:

復(fù)制代碼 代碼如下:

def sum_(lst):
    amount = 0
    for num in lst:
        amount = add(amount, num)
    return amount
 
print sum_(lst)

 

還能繼續(xù)。sum_函數(shù)定義了這樣一種流程:
1. 使用初始值與列表的第一個(gè)元素相加;
2. 使用上一次相加的結(jié)果與列表的下一個(gè)元素相加;
3. 重復(fù)第二步,直到列表中沒有更多元素;
4. 將最后一次相加的結(jié)果返回。

如果現(xiàn)在需要求乘積,我們可以寫出類似的流程——只需要把相加換成相乘就可以了:

 

復(fù)制代碼 代碼如下:

def multiply(lst):
    product = 1
    for num in lst:
        product = product * num
    return product

 

除了初始值換成了1以及函數(shù)add換成了乘法運(yùn)算符,其他的代碼全部都是冗余的。我們?yōu)槭裁床话堰@個(gè)流程抽象出來,而將加法、乘法或者其他的函數(shù)作為參數(shù)傳入呢?

復(fù)制代碼 代碼如下:

def reduce_(function, lst, initial):
    result = initial
    for num in lst:
        result = function(result, num)
    return result
 
print reduce_(add, lst, 0)


現(xiàn)在,想要算出乘積,可以這樣做:

復(fù)制代碼 代碼如下:

print reduce_(lambda x, y: x * y, lst, 1)

 

那么,如果想要利用reduce_找出列表中的最大值,應(yīng)該怎么做呢?請(qǐng)自行思考:)

雖然有模板方法這樣的設(shè)計(jì)模式,但那樣的復(fù)雜度往往使人們更情愿到處編寫循環(huán)。將函數(shù)作為參數(shù)完全避開了模板方法的復(fù)雜度。

Python有一個(gè)內(nèi)建函數(shù)reduce,完整實(shí)現(xiàn)并擴(kuò)展了reduce_的功能。本文稍后的部分包含了有用的內(nèi)建函數(shù)的介紹。請(qǐng)注意我們的目的是沒有循環(huán),使用函數(shù)替代循環(huán)是函數(shù)式風(fēng)格區(qū)別于指令式風(fēng)格的最顯而易見的特征。

*像Python這樣構(gòu)建于類C語言之上的函數(shù)式語言,由于語言本身提供了編寫循環(huán)代碼的能力,內(nèi)置函數(shù)雖然提供函數(shù)式編程的接口,但一般在內(nèi)部還是使用循環(huán)實(shí)現(xiàn)的。同樣的,如果發(fā)現(xiàn)內(nèi)建函數(shù)無法滿足你的循環(huán)需求,不妨也封裝它,并提供一個(gè)接口。

2.5. 作為返回值

將函數(shù)返回通常需要與閉包一起使用(即返回一個(gè)閉包)才能發(fā)揮威力。我們先看一個(gè)函數(shù)的定義:

復(fù)制代碼 代碼如下:

def map_(function, lst):
    result = []
    for item in lst:
        result.append(function(item))
    return result

 

函數(shù)map_封裝了最常見的一種迭代:對(duì)列表中的每個(gè)元素調(diào)用一個(gè)函數(shù)。map_需要一個(gè)函數(shù)參數(shù),并將每次調(diào)用的結(jié)果保存在一個(gè)列表中返回。這是指令式的做法,當(dāng)你知道了列表解析(list comprehension)后,會(huì)有更好的實(shí)現(xiàn)。

這里我們先略過map_的蹩腳實(shí)現(xiàn)而只關(guān)注它的功能。對(duì)于上一節(jié)中的lst,你可能發(fā)現(xiàn)最后求乘積結(jié)果始終是0,因?yàn)閘st中包含了0。為了讓結(jié)果看起來足夠大,我們來使用map_為lst中的每個(gè)元素加1:

 

復(fù)制代碼 代碼如下:

lst = map_(lambda x: add(1, x), lst)
print reduce_(lambda x, y: x * y, lst, 1)


答案是120,這還遠(yuǎn)遠(yuǎn)不夠大。再來:

復(fù)制代碼 代碼如下:

lst = map_(lambda x: add(10, x), lst)
print reduce_(lambda x, y: x * y, lst, 1)


囧,事實(shí)上我真的沒有想到答案會(huì)是360360,我發(fā)誓沒有收周鴻祎任何好處。

 

現(xiàn)在回頭看看我們寫的兩個(gè)lambda表達(dá)式:相似度超過90%,絕對(duì)可以使用抄襲來形容。而問題不在于抄襲,在于多寫了很多字符有木有?如果有一個(gè)函數(shù),根據(jù)你指定的左操作數(shù),能生成一個(gè)加法函數(shù),用起來就像這樣:

 

復(fù)制代碼 代碼如下:

lst = map_(add_to(10), lst) #add_to(10)返回一個(gè)函數(shù),這個(gè)函數(shù)接受一個(gè)參數(shù)并加上10后返回

 

寫起來應(yīng)該會(huì)舒服不少。下面是函數(shù)add_to的實(shí)現(xiàn):

 

復(fù)制代碼 代碼如下:

def add_to(n):
    return lambda x: add(n, x)


通過為已經(jīng)存在的某個(gè)函數(shù)指定數(shù)個(gè)參數(shù),生成一個(gè)新的函數(shù),這個(gè)函數(shù)只需要傳入剩余未指定的參數(shù)就能實(shí)現(xiàn)原函數(shù)的全部功能,這被稱為偏函數(shù)。Python內(nèi)置的functools模塊提供了一個(gè)函數(shù)partial,可以為任意函數(shù)生成偏函數(shù):

 

 

復(fù)制代碼 代碼如下:

functools.partial(func[, *args][, **keywords])

 

你需要指定要生成偏函數(shù)的函數(shù)、并且指定數(shù)個(gè)參數(shù)或者命名參數(shù),然后partial將返回這個(gè)偏函數(shù);不過嚴(yán)格的說partial返回的不是函數(shù),而是一個(gè)像函數(shù)一樣可直接調(diào)用的對(duì)象,當(dāng)然,這不會(huì)影響它的功能。

另外一個(gè)特殊的例子是裝飾器。裝飾器用于增強(qiáng)甚至干脆改變?cè)瘮?shù)的功能,我曾寫過一篇文檔介紹裝飾器,地址在這里:http://www.ythuaji.com.cn/article/59867.htm

*題外話,單就例子中的這個(gè)功能而言,在一些其他的函數(shù)式語言中(例如Scala)可以使用名為柯里化(Currying)的技術(shù)實(shí)現(xiàn)得更優(yōu)雅。柯里化是把接受多個(gè)參數(shù)的函數(shù)變換成接受一個(gè)單一參數(shù)(最初函數(shù)的第一個(gè)參數(shù))的函數(shù),并且返回接受余下的參數(shù)而且返回結(jié)果的新函數(shù)的技術(shù)。如下的偽代碼所示:

復(fù)制代碼 代碼如下:

#不是真實(shí)的代碼
def add(x)(y): #柯里化
    return x + y
 
lst = map_(add(10), lst)

 

通過將add函數(shù)柯里化,使得add接受第一個(gè)參數(shù)x,并返回一個(gè)接受第二個(gè)參數(shù)y的函數(shù),調(diào)用該函數(shù)與前文中的add_to完全相同(返回x + y),且不再需要定義add_to。看上去是不是更加清爽呢?遺憾的是Python并不支持柯里化。

2.6. 部分內(nèi)建函數(shù)介紹

reduce(function, iterable[, initializer])

這個(gè)函數(shù)的主要功能與我們定義的reduce_相同。需要補(bǔ)充兩點(diǎn):
它的第二個(gè)參數(shù)可以是任何可迭代的對(duì)象(實(shí)現(xiàn)了__iter__()方法的對(duì)象);
如果不指定第三個(gè)參數(shù),則第一次調(diào)用function將使用iterable的前兩個(gè)元素作為參數(shù)。
由reduce和一些常見的function組合成了下面列出來的內(nèi)置函數(shù):

復(fù)制代碼 代碼如下:

all(iterable) == reduce(lambda x, y: bool(x and y), iterable)
any(iterable) == reduce(lambda x, y: bool(x or y), iterable)
max(iterable[, args...][, key]) == reduce(lambda x, y: x if key(x) > key(y) else y, iterable_and_args)
min(iterable[, args...][, key]) == reduce(lambda x, y: x if key(x) < key(y) else y, iterable_and_args)
sum(iterable[, start]) == reduce(lambda x, y: x + y, iterable, start)

 

map(function, iterable, ...)

這個(gè)函數(shù)的主要功能與我們定義的map_相同。需要補(bǔ)充一點(diǎn):
map還可以接受多個(gè)iterable作為參數(shù),在第n次調(diào)用function時(shí),將使用iterable1[n], iterable2[n], ...作為參數(shù)。

filter(function, iterable)

這個(gè)函數(shù)的功能是過濾出iterable中所有以元素自身作為參數(shù)調(diào)用function時(shí)返回True或bool(返回值)為True的元素并以列表返回,與系列第一篇中的my_filter函數(shù)相同。

zip(iterable1, iterable2, ...)

這個(gè)函數(shù)返回一個(gè)列表,每個(gè)元素都是一個(gè)元組,包含(iterable1[n], iterable2[n], ...)。
例如:zip([1, 2], [3, 4]) --> [(1, 3), (2, 4)]
如果參數(shù)的長(zhǎng)度不一致,將在最短的序列結(jié)束時(shí)結(jié)束;如果不提供參數(shù),將返回空列表。

除此之外,你還可以使用本文2.5節(jié)中提到的functools.partial()為這些內(nèi)置函數(shù)創(chuàng)建常用的偏函數(shù)。

另外,pypi上有一個(gè)名為functional的模塊,除了這些內(nèi)建函數(shù)外,還額外提供了更多的有意思的函數(shù)。但由于使用的場(chǎng)合并不多,并且需要額外安裝,在本文中就不介紹了。但我仍然推薦大家下載這個(gè)模塊的純Python實(shí)現(xiàn)的源代碼看看,開闊思維嘛。里面的函數(shù)都非常短,源文件總共只有300行不到,地址在這里:http://pypi.python.org/pypi/functional

此篇結(jié)束:)

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 99re5精品视频在线观看 | 亚洲精品欧洲久久婷婷99 | japaneseles女同专区| 久久无码人妻中文国产 | 99国产精品免费观看视频 | 美女被的在线网站91 | 大色综合| 国产精品视频第一页 | 美女扒开腿让男人桶爽动态图片 | 无敌在线视频观看免费 | 亚洲一区二区精品视频 | 人与动人物人a级特片 | 国产精品久久现线拍久青草 | 闺蜜的样子小说安沁在线阅读 | 猛h辣h高h文湿校园1v1 | 情趣内衣在线观看 | 18捆绑调教在线高清 | 国产盗摄女厕美女嘘嘘 | 亚洲国产精品综合久久一线 | 亚洲精品成人AV在线观看爽翻 | 冰山美人调教耻辱h | 精品久久香蕉国产线看观看亚洲 | 日韩在线天堂免费观看 | 好猛好紧好硬使劲好大刺激视频 | tiny4k欧美极品在线 | 教师系列 大桥未久在线 | www.91在线| 国模娜娜一区二区三区 | 日本道在线播放 | 欧美精品一区二区三区免费观看 | 日本三级做a全过程在线观看 | 国产精品视频在线观看 | 日韩在线观看一区二区不卡视频 | 久青草国产在线观看视频 | 欧美高清在线精品一区二区不卡 | 天天色综合色 | 青青网在线视频 | 成人做视频免费 | 成年人黄视频在线观看 | 成人男女网免费 | 免费观看全集 |