Python 函數(shù)式編程-高階函數(shù)

文中知識(shí)點(diǎn)和代碼示例學(xué)習(xí)自慕課網(wǎng),python進(jìn)階部分(http://www.imooc.com/learn/317) 學(xué)習(xí)筆記

把函數(shù)作為參數(shù)

例1:求兩個(gè)數(shù)字絕對(duì)值的和(abs(x)+abs(y))

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

# add 函數(shù)的x,y,f 參數(shù)可以是任何值,如果 f 是個(gè)函數(shù),則 x,y 兩個(gè)參數(shù)分別帶入到 f 函數(shù)中求值后,再做為 add 函數(shù)的參數(shù)傳入。
print add(-5,9,abs)

例2:求√x + √y的值

import math

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

print add(25, 9, math.sqrt)

map() 函數(shù)

它接收一個(gè)函數(shù)f和一個(gè)list,并通過(guò)把函數(shù)f依次作用在list的每個(gè)元素上,得到一個(gè)新的list 并返回。

例1:把列表總每個(gè)元素都取二次方

def f(x):
    return x * x
print map(f,[1,2,3,4,5,6,7,8,9])

#[1, 4, 9, 16, 25, 36, 49, 64, 81]

例2:將列表中的字符串都變?yōu)槭鬃帜复髮?/p>

def format_name(s):
    return s.title()
print map(format_name, ['adam', 'LISA', 'barT'])

#['Adam', 'Lisa', 'Bart']

reduce() 函數(shù)

reduce()函數(shù)接收的參數(shù)和map()類似,一個(gè)函數(shù)f,一個(gè)list,但行為和map()不同,reduce()傳入的函數(shù)f必須接收兩個(gè)參數(shù),reduce()對(duì)list的每個(gè)元素反復(fù)調(diào)用函數(shù)f,并返回最終結(jié)果值。

例1:求一個(gè)列表中所有元素的和

def f(x,y):
    return x + y
print reduce(f,[1,2,3,4,5])

先計(jì)算頭兩個(gè)元素:f(1, 2),結(jié)果為3;
再把結(jié)果和第3個(gè)元素計(jì)算:f(3, 3),結(jié)果為6;
再把結(jié)果和第4個(gè)元素計(jì)算:f(6, 4),結(jié)果為10;
再把結(jié)果和第5個(gè)元素計(jì)算:f(10, 5),結(jié)果為15;
由于沒(méi)有更多的元素了,計(jì)算結(jié)束,返回結(jié)果15.

#15

filter() 函數(shù)

filter()函數(shù)接收一個(gè)函數(shù)f和一個(gè)list,這個(gè)函數(shù)f的作用是對(duì)每個(gè)元素進(jìn)行判斷,返回TrueFalse,filter()根據(jù)判斷結(jié)果自動(dòng)過(guò)濾掉不符合條件的元素,返回由符合條件元素組成的新list

例:刪除一個(gè)list中的偶數(shù),保留奇數(shù)

def is_odd(x):
    return x % 2 == 1
print filter(is_odd(),[1,2,3,4,5,6,6,7,8,9])

#[1, 3, 5, 7, 9]

可以用來(lái)刪除一個(gè)列表中我們不需要的元素。也可以使用如下方法來(lái)完成:

[i for i in [1,2,3,4,5,6,6,7,8,9] if i % 2 ==1 ]

#[1, 3, 5, 7, 9]

sorted() 函數(shù)

Python內(nèi)置的 sorted()函數(shù)可對(duì)list進(jìn)行排序:

>>>sorted([36, 5, 12, 9, 21])
[5, 9, 12, 21, 36]

但 sorted()也是一個(gè)高階函數(shù),它可以接收一個(gè)比較函數(shù)來(lái)實(shí)現(xiàn)自定義排序,比較函數(shù)的定義是,傳入兩個(gè)待比較的元素 x, y,如果 x 應(yīng)該排在 y 的前面,返回 -1,如果 x 應(yīng)該排在 y 的后面,返回 1。如果 x 和 y 相等,返回 0。

例:實(shí)現(xiàn)倒序排列

def reversed_cmp(x, y):
    if x > y:
        return -1
    if x < y:
        return 1
    return 0

print sorted([36, 5, 12, 9, 21], reversed_cmp)

#[36, 21, 12, 9, 5]

其他方法:

# 先排序,后反轉(zhuǎn)
>>> sorted([36, 21, 12, 9, 5])[::-1]
[36, 21, 12, 9, 5]
# 使用reversed()方法,返回一個(gè)迭代器。 如果使用reverse(),是直接修改原列表,不會(huì)返回新的列表
>>> list(reversed(sorted([36, 5, 12, 9, 21])))
[36, 21, 12, 9, 5]

返回函數(shù)

Python的函數(shù)不但可以返回intstr、list、dict等數(shù)據(jù)類型,還可以返回函數(shù)!

例1:定義一個(gè)函數(shù) f(),我們讓它返回一個(gè)函數(shù) g

def f():
    print 'call f()...'
    # 定義函數(shù)g:
    def g():
        print 'call g()...'
    # 返回函數(shù)g:
    return g
    
>>> x = f()   # 調(diào)用f()
call f()...
>>> x   # 變量x是f()返回的函數(shù)g:
<function g at 0x1037bf320>
>>> x()   # x指向函數(shù)g,因此可以調(diào)用
call g()...   # 調(diào)用x()就是執(zhí)行g(shù)()函數(shù)定義的代碼

例2:寫一個(gè)函數(shù)calc_prod(lst),它接收一個(gè)list,返回一個(gè)函數(shù),返回函數(shù)可以計(jì)算參數(shù)的乘積。

def calc_prod(lst):
    def f(x,y):
        return x * y
    def chengji():
        return reduce(f,lst)
    return chengji

f = calc_prod([1, 2, 3, 4])
print f()

閉包

例如上一小節(jié)返回函數(shù)中的例2,chengji內(nèi)層函數(shù)引用了外層函數(shù)calc_prodlst變量。像這種內(nèi)層函數(shù)引用了外層函數(shù)的變量(參數(shù)也算變量),然后返回內(nèi)層函數(shù)的情況,稱為閉包(Closure)。

閉包的特點(diǎn):返回的函數(shù)還引用了外層函數(shù)的局部變量,所以,要正確使用閉包,就要確保引用的局部變量在函數(shù)返回后不能變。舉例如下:

# 希望一次返回3個(gè)函數(shù),分別計(jì)算1x1,2x2,3x3:
def count():
    fs = []
    for i in range(1, 4):
        def f():
             return i*i
        fs.append(f)
    return fs

f1, f2, f3 = count()

#9 9 9

原因就是當(dāng)count()函數(shù)返回了3個(gè)函數(shù)時(shí),這3個(gè)函數(shù)所引用的變量i的值已經(jīng)變成了3。由于f1、f2、f3并沒(méi)有被調(diào)用,所以,此時(shí)他們并未計(jì)算i*i,當(dāng) f1 被調(diào)用時(shí):

>>> f1()
9     # 因?yàn)閒1現(xiàn)在才計(jì)算i*i,但現(xiàn)在i的值已經(jīng)變?yōu)?

因此,返回函數(shù)不要引用任何循環(huán)變量,或者后續(xù)會(huì)發(fā)生變化的變量。因此改成如下代碼:

# 法一
def count():
    fs = []
    for i in range(1, 4):
        def f(j):
        # 借助函數(shù)f來(lái)避免引用循環(huán)變量i
            def g():
                return j*j
            return g
        r = f(i)
        fs.append(r)
    return fs

f1, f2, f3 = count()
print f1(), f2(), f3()

#1 4 9

# 法二
def count():
    fs = []
    def f(j):
        def g():
            return j*j
        return g

    for i in range(1, 4):
        r = f(i)
        fs.append(r)
    return fs

f1, f2, f3 = count()
print f1(), f2(), f3()

#1 4 9

匿名函數(shù) lambda

高階函數(shù)可以接收函數(shù)做參數(shù),有些時(shí)候,我們不需要顯式地定義函數(shù),直接傳入匿名函數(shù)更方便。

Python中,對(duì)匿名函數(shù)提供了有限支持。還是以map()函數(shù)為例,計(jì)算f(x)=x*x時(shí),除了定義一個(gè)f(x)的函數(shù)外,還可以直接傳入匿名函數(shù)

>>> a = lambda x:x*x
>>> a(5)
25
>>> map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9])
[1, 4, 9, 16, 25, 36, 49, 64, 81]

匿名函數(shù)有個(gè)限制,就是只能有一個(gè)表達(dá)式,不寫return,返回值就是該表達(dá)式的結(jié)果。

def is_not_empty(s):
    return s and len(s.strip()) > 0
filter(is_not_empty, ['test', None, '', 'str', '  ', 'END'])

返回函數(shù)的時(shí)候,也可以返回匿名函數(shù):

# 使用 if..else 來(lái)實(shí)現(xiàn)abs函數(shù)的功能
>>> myabs = lambda x: -x if x < 0 else x 
>>> myabs(-1)
1
>>> myabs(1)
1

例:使用匿名函數(shù)簡(jiǎn)化代碼

# 源代碼
def is_not_empty(s):
    return s and len(s.strip()) > 0
filter(is_not_empty, ['test', None, '', 'str', '  ', 'END'])

# 使用匿名函數(shù)
print filter(lambda x:x and len(x.strip()) > 0,['test', None, '', 'str', '  ', 'END'])

# 使用 for..in..if 語(yǔ)句
print [i for i in ['test', None, '', 'str', '  ', 'END'] if i and len(i.strip())>0]

偏函數(shù)

當(dāng)一個(gè)函數(shù)有很多參數(shù)時(shí),調(diào)用者就需要提供多個(gè)參數(shù)。如果減少參數(shù)個(gè)數(shù),就可以簡(jiǎn)化調(diào)用者的負(fù)擔(dān)。

比如,int()函數(shù)可以把字符串轉(zhuǎn)換為整數(shù),當(dāng)僅傳入字符串時(shí),int()函數(shù)默認(rèn)按十進(jìn)制轉(zhuǎn)換:

>>> int('12345')
12345

int()函數(shù)還提供額外的base參數(shù),默認(rèn)值為10。如果傳入base參數(shù),就可以做 N 進(jìn)制的轉(zhuǎn)換:

>>> int('12345', base=8)
5349
>>> int('12345', 16)
74565

假設(shè)要轉(zhuǎn)換大量的二進(jìn)制字符串,每次都傳入int(x, base=2)非常麻煩,于是,我們想到,可以定義一個(gè)int2()的函數(shù),默認(rèn)把base=2傳進(jìn)去:

def int2(x, base=2):
    return int(x, base)
    
# 這樣,我們轉(zhuǎn)換二進(jìn)制就非常方便了:
>>> int2('1000000')
64
>>> int2('1010101')
85

functools.partial就是幫助我們創(chuàng)建一個(gè)偏函數(shù)的,不需要我們自己定義int2(),可以直接使用下面的代碼創(chuàng)建一個(gè)新的函數(shù)int2

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

所以,functools.partial可以把一個(gè)參數(shù)多的函數(shù)變成一個(gè)參數(shù)少的新函數(shù),少的參數(shù)需要在創(chuàng)建時(shí)指定默認(rèn)值,這樣,新函數(shù)調(diào)用的難度就降低了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容