當前位置:
首頁 > 知識 > Python學習之函數式編程

Python學習之函數式編程


Linux編程

點擊右側關注,免費入門到精通!




作者丨stone_zhu



https://www.jianshu.com/p/efa8925e0256




高階函數




在python中函數名是指向函數的變數,當函數的參數也是函數的時候,這種函數我們稱之為高階函數。



def

 

add

(x, y, f)

:


    

return

 f(x) + f(y)
add(

1

-6

, abs)



map/reduce



map()函數接收兩個參數,一個是函數,一個是Iterable,map將傳入的函數依次作用到序列的每個元素,並把結果作為新的Iterator返回。




def

 

f

(x)

:
    

return

 x * x
r = map(f, [

1

2

3

4

5

6

7

8

9

])
print(list(r))

>>

> [

1

4

9

16

25

36

49

64

81

]



reduce把一個函數作用在一個序列[x1, x2, x3, ...]上,這個函數必須接收兩個參數,reduce把結果繼續和序列的下一個元素做累積計算,效果如下:



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



結合map和reduce可以整理出一個str2int的函數:



from

 functools 

import

 reduce

DIGITS = {

"0"

0

"1"

1

"2"

2

"3"

3

"4"

4

"5"

5

"6"

6

"7"

7

"8"

8

"9"

9

}

def

 

char2num

(s)

:


    

return

 DIGITS[s]

def

 

str2int

(s)

:


    

return

 reduce(

lambda

 x, y: x * 

10

 + y, map(char2num, s))





filter




Python內建的filter()函數用於過濾序列。




和map()類似,filter()也接收一個函數和一個序列。和map()不同的是,filter()把傳入的函數依次作用於每個元素,然後根據返回值是True還是False決定保留還是丟棄該元素。




例如,在一個list中,刪掉偶數,只保留奇數,可以這麼寫:



def

 

is_odd

(n)

:


    

return

 n % 

2

 == 

1

list(filter(is_odd, [

1

2

4

5

6

9

10

15

]))

# 結果: [1, 5, 9, 15]







sorted




對list進行排序:

>>> 

sorted

(

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

)

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





此外,sorted()函數也是一個高階函數,它還可以接收一個key函數來實現自定義的排序,例如按絕對值大小排序:



>>

> sorted([

36

5

, -

12

9

, -

21

], key=abs)
[

5

9

, -

12

, -

21

36

]



字元串排序:



>>

> sorted([

"bob"

"about"

"Zoo"

"Credit"

])
[

"Credit"

"Zoo"

"about"

"bob"

]



忽略大小寫:



>>

> sorted([

"bob"

"about"

"Zoo"

"Credit"

], key=str.lower)
[

"about"

"bob"

"Credit"

"Zoo"

]



要進行反向排序,不必改動key函數,可以傳入第三個參數reverse=True



>>

> sorted([

"bob"

"about"

"Zoo"

"Credit"

], key=str.lower, reverse=True)
[

"Zoo"

"Credit"

"bob"

"about"

]



返回函數




定義




高階函數除了可以接受函數作為參數外,還可以把函數作為結果值返回。


我們來實現一個可變參數的求和。通常情況下,求和的函數是這樣定義的:

def

 

calc_sum

(*args)

:


    ax = 

0


    

for

 n 

in

 args:
        ax = ax + n
    

return

 ax



但是,如果不需要立刻求和,而是在後面的代碼中,根據需要再計算怎麼辦?可以不返回求和的結果,而是返回求和的函數:



def

 

lazy_sum

(*args)

:


    

def

 

sum

()

:


        ax = 

0


        

for

 n 

in

 args:
            ax = ax + n
        

return

 ax
    

return

 sum



當我們調用lazy_sum()時,返回的並不是求和結果,而是求和函數:



>>

> f = lazy_sum(

1

3

5

7

9

)

>>

> f
<function lazy_sum.<locals>.sum at 

0x101c6ed90

>



調用函數f時,才真正計算求和的結果



>>

> f()

25







閉包




如上所示的例子中,我們在函數lazy_sum中又定義了函數sum,並且,內部函數sum可以引用外部函數lazy_sum的參數和局部變數,當lazy_sum返回函數sum時,相關參數和變數都保存在返回的函數中,這種稱為「閉包(Closure)」。




注意點:返回函數不要引用任何循環變數,或者後續會發生變化的變數。


如下所示:



def

 

count

()

:


    fs = []
    

for

 i 

in

 range(

1

4

):
        

def

 

f

()

:


             

return

 i*i
        fs.append(f)
    

return

 fs

f1, f2, f3 = count()



返回結果如下:



>>

> f1()

9


>>

> f2()

9


>>

> f3()

9





匿名函數




關鍵字lambda表示匿名函數,冒號前面的x表示函數參數。匿名函數有個限制,就是只能有一個表達式,不用寫return,返回值就是該表達式的結果。

>>> 

list

(

map

(lambda x: x * x, [

1

2

3

4

5

6

7

8

9

]))
[

1

4

9

16

25

36

49

64

81

]



裝飾器




代碼運行期間動態增加功能的方式,稱之為「裝飾器」(Decorator)。本質上,decorator就是一個返回函數的高階函數。


定義一個能列印日誌的decorator,可以定義如下:

def

 

log

(func)

:


    

def

 

wrapper

(*args, **kw)

:


        print(

"call %s():"

 % func.__name__)
        

return

 func(*args, **kw)
    

return

 wrapper



觀察上面的log,因為它是一個decorator,所以接受一個函數作為參數,並返回一個函數。我們要藉助Python的@語法,把decorator置於函數的定義處:



@log

def

 

now

()

:
    print(

"2015-3-25"

)

>>

> now()
call now():

2015

-

3

-

25





把@log放到now()函數的定義處,相當於執行了語句:now = log(now),now變數指向了新的函數。


如果decorator本身需要傳入參數,那就需要編寫一個返回decorator的高階函數,寫出來會更複雜。比如,要自定義log的文本:

def

 

log

(text)

:


    

def

 

decorator

(func)

:


        

def

 

wrapper

(*args, **kw)

:


            print(

"%s %s():"

 % (text, func.__name__))
            

return

 func(*args, **kw)
        

return

 wrapper
    

return

 decorator



這個3層嵌套的decorator用法如下:



@log(

"execute"

)

def

 

now

()

:
    print(

"2015-3-25"

)

>>

> now()
execute now():

2015

-

3

-

25





和兩層嵌套的decorator相比,3層嵌套的效果是這樣的:>>> now = log("execute")(now)


有個問題,經過decorator裝飾之後的函數,它們的

name

已經從原來的"now"變成了"wrapper":

>>

> now.__name_

_


"wrapper"





Python內置的functools.wraps用來複制函數name屬性的:



import

 functools

def

 

log

(func)

:


    @functools.wraps(func)
    

def

 

wrapper

(*args, **kw)

:


        print(

"call %s():"

 % func.__name__)
        

return

 func(*args, **kw)
    

return

 wrapper



或者針對帶參數的裝飾器:



import

 functools

def

 

log

(text)

:


    

def

 

decorator

(func)

:


        @functools.wraps(func)
        

def

 

wrapper

(*args, **kw)

:


            print(

"%s %s():"

 % (text, func.__name__))
            

return

 func(*args, **kw)
        

return

 wrapper
    

return

 decorator



偏函數




Python的functools模塊提供了很多有用的功能,其中一個就是偏函數(Partial function)。把一個函數的某些參數給固定住(也就是設置默認值),返回一個新的函數,調用這個新函數會更簡單。




創建偏函數時,實際上可以接收函數對象、args和*kw這3個參數。




比如要使用int()函數去轉換二進位字元串,原本的寫法是:int(x, base=2),如果量大的話,很麻煩,所以我們可以這麼寫:



>>

> import functools

>>

> int2 = functools.partial(int, base=

2

)

>>

> int2(

"1000000"

)

64


>>

> int2(

"1010101"

)

85





上面的in2 = functools.partial(int, base=2)相當於:



kw = { 

"base"

2

 }

int

(

"10010"

, **kw)



【點擊成為編程大牛】

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 Python開發 的精彩文章:

Python面試必須要看的15個問題
你沐浴後的味道,根本讓人把持不住!留香24小時,比噴了大牌香水還迷人!

TAG:Python開發 |