在介紹函數式程式設計之前,先介紹幾個概念性的東西。
什麼是函數式程式設計?
函數式程式設計的特點:
1.把計算視為函數而非指令;
2.純函數式程式設計:不需要變量,沒有副作用,測試簡單;
3.支援高階函數,代碼簡潔。
什麼是高階函數?
能接收函數做參數的函數,稱為高階函數。
高階函數的特點:
1.變量可以指向函數
2.函數的參數可以接收變量
3.一個函數可以接收另一個函數作為參數
Python支援的函數式程式設計?
1.不是純函數式程式設計:允許有變量
2.支援高階函數:函數也可以作為變量傳入
3.支援閉包:有了閉包就能傳回函數
4.有限度的支援匿名函數
關于匿名函數和閉包的介紹,後面會一一介紹,暫時不在這裡詳細介紹。
高階函數-把函數作為參數
>>> def fn(x,y,f):
... return f(x)+f(y)
...
>>> import math
>>> fn(4,16,math.sqrt)
6.0
python内置的幾個常用的高階函數
map():接收一個函數f和一個list對象,函數f作用于list對象的每一個元素,并傳回一個新的list對象。
>>> def fn(x):
... return x*x;
...
>>> print map(fn,range(1,11))
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
reduce():也是接收函數f和一個list對象,但是接收函數f必須傳入兩個參數,然後對lsit中的每一個元素反複調用函數f,并傳回最終結果值。
>>> def prod(x,y):
... return x*y
...
>>> print reduce(prod,range(1,6))
120 //1 *2*3*4*5
filter():接收函數f和一個list對象,函數f的作用是對每一個list對象的元素進行判斷,傳回true 和false。filter()根據判斷結果自動過濾掉不符合的元素,傳回符合條件的元素組成的新list。
>>> def is_even_number(x):
... return x%2==0
...
>>> filter(is_even_number,range(1,11))
[2, 4, 6, 8, 10]
上面針對高階函數講解,大家應該了解了函數是可以作為參數的,那麼既然函數可以做參數,那麼也可以傳回函數。
傳回函數
>>> def f():
... print 'call f()....'
... //定義函數g:
... def g():
... print 'call g()....'
//傳回函數g:
... return g
...
>>> x = f() //調用f()
call f()....
>>> x
<function g at 0x7f62fab01c80>
>>> x() //調用x()就是執行g()函數定義的代碼
call g()....
舉個例子:編寫一個函數,接收一個list,傳回一個函數,傳回函數可以計算參數的乘積。
>>> def fn(x,y):
... return x*y;
...
>>> def calc_prod(lst):
... def my_calc():
... return reduce(fn,lst)
... return my_calc
...
>>> f = calc_prod(range(1,5))
>>> print f()
24
閉包
内層函數引用外層函數的變量,然後傳回内層函數的情況,稱為閉包。
閉包的特點:傳回的函數引用了外層函數的局部變量。是以,要正确使用閉包,就能確定引用的局部變量在函數傳回後不能改變。舉例說明:
//希望一次傳回3個函數,分别計算1x1,2x2,3x3
>>> def count():
... lst = []
... for i in range(1,4):
... def f():
... return i*i
... lst.append(f)
... return lst
...
>>> f1,f2,f3 = count()
>>> f1()
9
>>> f2()
9
>>> f3()
9
得到的結果并不是我們預期的1,4,9.
lst.append(f),隻是将每一個f()的引用儲存進了list,并沒有進行對于i的計算,是以導緻最後在運作了f(1)之後,i已經變為了3.
是以,傳回函數不要引用任何循環變量,或者後續會發生變化的變量。
那麼怎麼改寫上述的代碼,得到想要的1,4,9.值。
方法一:
>>> def count():
... lst = []
... for i in range(1,4):
... def f():
... return i*i
... lst.append(f()) //很大的不同,f 變為了f(),這一步的時候已經進行了i*i的運算
... return lst
...
>>> f1,f2,f3 = count()
>>> print f1,f2,f3
1 4 9
方法二:
>>> def count():
... lst = []
... for i in range(1,4):
... def f(m=i)://傳入參數
... return m*m
... lst.append(f)
... return lst
...
>>> f1,f2,f3 = count()
>>> print f1(),f2(),f3()
1 4 9
問題的産生是因為函數隻在執行時才去擷取外層參數i,若函數定義時可以擷取到i,問題便可解決。而預設參數正好可以完成定義時擷取i值且運作函數時無需參數輸入的功能,是以在函數f()定義中改為f(m = i),函數f傳回值改為m*m即可.
匿名函數
關鍵字 lambda表示匿名函數,冒号前面的x表示函數參數。
匿名函數有個限制,就是隻能有一個表達式,不能寫return,傳回值就直接寫表達式的結果。
例子一:
>>> map(lambda x:x*x,range(1,5))
[1, 4, 9, 16]
lambda x:x*x,可以看做是
>>> def f(x):
... return x*x
例子二:
>>> myabs = lambda x:-x if x<0 else x
>>> myabs(-1)
1
>>> myabs(2)
2
lambda x:-x if x<0 else x可以看做是
>>> def f(x):
... x = -x
... if x<0:
... return -x
... else:
... return x
小結:這一節主要講解python函數式程式設計概念及其python高階函數的定義及其使用。