本文實例講述了Python函數式編程。分享給大家供大家參考,具體如下:
函數式編程就是一種抽象程度很高的編程范式,從計算機硬件->匯編語言->C語言->Python抽象程度越高、越貼近于計算,但執行效率也越低。純粹的函數式編程語言編寫的函數沒有變量,因此,任意一個函數,只要輸入是確定的,輸出就是確定的,這種純函數我們稱之為沒有副作用。而允許使用變量的程序設計語言,由于函數內部的變量狀態不確定,同樣的輸入,可能得到不同的輸出,因此,這種函數是有副作用的。函數式編程的一個特點就是,允許把函數本身作為參數傳入另一個函數,還允許返回一個函數!
Python對函數式編程提供部分支持,支持高階函數(函數可以作為變量傳入),支持閉包(返回一個函數),有限地支持匿名函數。由于Python允許使用變量,因此,Python不是純函數式編程語言。
1、高階函數
即可以通過變量名指向函數,函數通過變量名作為參數傳給另一個函數,并通過變量名來使用。例如下面將開方函數math.sqrt作為參數傳遞給變量f,變量名f就指向了函數math.sqrt,再通過變量f使用該函數給x、y開方。
1
2
3
4
5
|
import math def add(x, y, f): return f(x) + f(y) # 函數作為參數傳遞給f來調用 res = add( 25 , 9 , math.sqrt) print (res) |
map函數接收一個函數 f 和一個 list,并把函數 f 依次作用在 list 的每個元素上,得到一個iterators并返回。
1
2
3
4
|
def format_name(s): return s[ 0 ].upper() + s[ 1 :].lower() #將列表的每個元素首字母大寫,其他小寫 print ( list ( map (format_name, [ 'adam' , 'LISA' , 'barT' ]))) #輸出:['Adam', 'Lisa', 'Bart'] |
filter()根據判斷函數f的結果自動過濾掉不符合條件的元素,以iterators返回剩下的元素
1
2
3
4
|
def is_odd(x): return x % 2 = = 1 # 過濾函數,x為奇返回True f_res = filter (is_odd, [ 1 , 4 , 6 , 7 , 9 , 12 , 17 ]) print ( list (f_res)) # 輸出過濾后的結果list:1 7 9 17 |
sorted()函數用于對可迭代的對象進行排序,參數key=指定排序的關鍵字,這里可以借助functools.cmp_to_keys()將比較方法映射為自定義的方法。例如實現了降序排列,比較函數cmp返回值 -1 代表a 應該排在 b 的前面,如果a排在b 的后面返回 1。如果 a、b相等返回 0。
1
2
3
4
5
6
7
8
9
|
import functools def cmp (a, b): if b < a: return - 1 if a < b: return 1 return 0 a = [ 1 , 2 , 5 , 4 ] print ( sorted (a, key = functools.cmp_to_key( cmp ))) |
2、匿名函數和閉包
有時函數簡單到只有一個表達式時,為了簡化代碼可以使用匿名函數來代替,匿名函數一般形式為lambda 參數:返回表達式,例如lambda x:x*x,就是傳入x參數并返回x的平方。例如在使用map()函數時需要傳入一個函數用于list的元素,此時可以使用匿名函數作為參數
1
2
3
|
lst = [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ] res = map ( lambda x: x * x, lst) # 將匿名函數作用于lst print ( list (res)) |
函數的閉包(Closure)是指內層函數引用了外層函數的變量,然后將內層函數像變量一樣返回的情況。例如函數calc_prod()接收一個list,在其內部定義一個函數multiply,計算list元素的乘積并將multiply返回。用f接收calc_prod()的返回函數,并在之后調用該函數
1
2
3
4
5
6
7
8
9
|
def calc_prod(lst): def multiply(): res = 1 for i in lst: res = res * i return res return multiply # 將函數返回 f = calc_prod([ 1 , 2 , 3 , 4 ]) # 接收返回函數 print (f()) # 調用返回函數 |
注意在函數閉包時要確保引用的局部變量在函數返回后不能變。例如下面的例子,當count()函數返回3個函數時,由于f1、f2、f3并沒有被調用,所以并未計算 i*i。當 f1 被調用時,這3個函數所引用的變量 i 的值已經變成了3,所以此時使用的變量i的值已經發生了改變,三個函數的輸出都是9。
1
2
3
4
5
6
7
8
9
10
|
def count(): fs = [] for i in range ( 1 , 4 ): def f(): print (i) # 函數f1()調用時i已經變為3 return i * i fs.append(f) return fs f1, f2, f3 = count() print (f1()) # 輸出9而不是1 |
3、函數裝飾器
函數裝飾器是指在原有函數的基礎上對函數作修改和裝飾操作。其基本思想是,既然函數可以像變量一樣作為參數傳入并且返回,那么我們可以將原來的函數傳入裝飾器函數,然后增加我們需要的操作,之后在將原函數返回出來。
例如下面定義了一個裝飾器log用于打印函數名稱,原函數作為參數f傳入。在裝飾器中定義新的函數fn,其中參數列*args和**kw代表自適應參數個數,防止不同參數個數的函數在使用裝飾器時不匹配。在新函數fn中輸出原函數的名稱,之后將原函數原封不動地調用一遍并返回出去。最后返回新函數。
在使用裝飾器時,只需要在函數的定義前加一行@裝飾器名
1
2
3
4
5
6
7
8
9
|
def log(f): # 定義裝飾器log def fn( * args, * * kw): # 定義新函數 print ( '函數名: ' + f.__name__) # 打印函數名 return f( * args, * * kw) # 在新函數中調用原函數并返回結果 return fn # 返回新函數 @log # 為函數add添加裝飾器 def add(x, y): return x + y print (add( 1 , 2 )) |
如果希望給裝飾器傳入一個參數,則需要定義三重嵌套的函數,在最外層增加一層函數用于接收參數。例如希望在打印函數名之前輸出傳入的參數“DEBUG”
1
2
3
4
5
6
7
8
9
10
11
|
def log(prefix): def log_decorator(f): def wrapper( * args, * * kw): print '[%s] %s()...' % (prefix, f.__name__) return f( * args, * * kw) return wrapper return log_decorator @log ( 'DEBUG' ) # 為裝飾器傳入參數 def test(): pass test() |
由于裝飾器實際上是創建了新的函數fn并替代了原函數,所以原函數的相關信息例如函數名會被覆蓋,可以用@functools.wraps(f)來復制原函數的信息以保留下來。
1
2
3
4
5
6
7
|
import functools def log(f): @functools .wraps(f) def fn( * args, * * kw): print 'call...' return f( * args, * * kw) return fn |
偏函數可以為函數填上一個固定的參數值,從而生成一個新的函數。例如原函數add需要兩個參數x、y,通過指定y=1得到偏函數add1,這個函數只需要輸入一個參數x,從而計算x+1的值。
1
2
3
4
5
|
import functools def add(x, y): return x + y add1 = functools.partial(add, y = 1 ) print (add2( 3 )) # 輸出結果為4 |
希望本文所述對大家Python程序設計有所幫助。
原文鏈接:https://blog.csdn.net/theVicTory/article/details/95070465