本篇文章咱們來介紹下Python函數式編程的知識。最主要的一點,Python中的函數是對象,能夠複製給變量!好了,咱們來介紹幾個Python函數式編程中的要點,包括高階函數、匿名函數、裝飾器、偏函數等等。精彩內容,不容錯過!編程
函數自己也能夠賦值給變量,即:變量能夠指向函數。若是一個變量指向了一個函數,那麼,能夠經過該變量來調用這個函數。閉包
f = abs f(-10) # 10
既然變量能夠指向函數,函數的參數能接收變量,那麼一個函數就能夠接收另外一個函數做爲參數,這種函數就稱之爲高階函數。app
def add_abs(x,y,f): return f(x) + f(y) f = abs add_abs(-5,6,f) # 11
接下來,咱們介紹幾個重點的高階函數。函數式編程
map函數函數
map()函數接收兩個參數,一個是函數,一個是Iterable,map將傳入的函數依次做用到序列的每一個元素,並把結果做爲新的Iterator返回。咱們以前介紹過了,Iterator是惰性序列,須要經過list()函數讓它把返回結果變爲list。學習
def f(x): return x * x r = list(map(f,[1,2,3,4,5,6,7,8,9,10])) r
輸出結果爲:ui
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
reduce函數spa
reduce把一個函數做用在一個序列[x1, x2, x3, ...]上,這個函數必須接收兩個參數,reduce把結果繼續和序列的下一個元素作累積計算:視頻
def f(x,y): return x + y reduce(f,[1,2,3,4,5]) # 15
filter函數對象
和map()相似,filter()也接收一個函數和一個序列。和map()不一樣的是,filter()把傳入的函數依次做用於每一個元素,而後根據返回值是True仍是False決定保留仍是丟棄該元素。filter()函數返回的是一個Iterator,也就是一個惰性序列,一樣須要經過list()函數讓它把返回結果變爲list。
def is_odd(n): return n%2==1 list(filter(is_odd,[1,2,3,4,5,6,7,8,9]))
結果爲:
[1, 3, 5, 7, 9]
sorted函數
sorted()函數也是一個高階函數,它還能夠接收一個key函數來實現自定義的排序,例如按絕對值大小排序或者按照小寫字母順序進行排序:
print(sorted([36,5,-12,9,-21],key=abs)) print(sorted(['bob', 'about', 'Zoo', 'Credit'],key=str.lower))
結果爲:
[5, 9, -12, -21, 36] ['about', 'bob', 'Credit', 'Zoo']
高階函數除了能夠接受函數做爲參數外,還能夠把函數做爲結果值返回。
咱們來實現一個可變參數的求和,若是不須要馬上求和,而是在後面的代碼中,根據須要再計算怎麼辦?能夠不返回求和的結果,而是返回求和的函數:
def lazy_sum(*args): def sum(): res = 0 for n in args: res += n return res return sum f = lazy_sum(1,3,5,7,9) f()
在這個例子中,咱們在函數lazy_sum中又定義了函數sum,
而且,內部函數sum能夠引用外部函數lazy_sum的參數和局部變量,
當lazy_sum返回函數sum時,相關參數和變量都保存在返回的函數中,
這種稱爲「閉包(Closure)」的程序結構擁有極大的威力。
請再注意一點,當咱們調用lazy_sum()時,每次調用都會返回一個新的函數,即便傳入相同的參數:
f1 = lazy_sum(1,3,5,7,9) f2 = lazy_sum(1,3,5,7,9) f1 == f2 # False
另外一個須要注意的問題是,返回的函數並無馬上執行,而是直到調用了f()才執行。咱們來看一個例子:
def count(): fs = [] for i in range(1,4): def f(): return i * i fs.append(f) return fs f1,f2,f3 = count() print(f1()) print(f2()) print(f3())
輸出結果爲:
9 9 9
所有都是9!緣由就在於返回的函數引用了變量i,但它並不是馬上執行。等到3個函數都返回時,它們所引用的變量i已經變成了3,所以最終結果爲9。若是必定要引用循環變量怎麼辦?方法是再建立一個函數,用該函數的參數綁定循環變量當前的值,不管該循環變量後續如何更改,已綁定到函數參數的值不變:
def count(): fs = [] def sub(j): def f(): return j * j return f for i in range(1,4): fs.append(sub(i)) return fs f1,f2,f3 = count() print(f1()) print(f2()) print(f3()
結果爲:
1 4 9
當咱們在傳入函數時,有些時候,不須要顯式地定義函數,直接傳入匿名函數更方便。相信你們對於匿名函數必定不陌生,其實就是咱們常說的lambda函數:
list(map(lambda x:x * x,[1,2,3,4,5,6,7,8,9])) def build(x,y): return lambda:x * x + y * y reduce(lambda x,y:x + y,[1,2,3,4,5,6,7,8,9])
在代碼運行期間動態增長功能的方式,稱之爲「裝飾器」(Decorator)。
本質上,decorator就是一個返回函數的高階函數。
因此,假設咱們要定義一個可以打印當前函數名的decorator:
def log(func): def wrapper(*args,**kwargs): print('call %s()' % func.__name__) return func(*args,**kwargs) return wrapper @log def helloworld(): print('hello world') helloworld()
執行結果爲:
call helloworld() hello world
下面的例子中,正是因爲wrapper中把 func(args,*kwargs)進行return,所以函數得以執行。
若是decorator自己須要傳入參數,那就須要編寫一個返回decorator的高階函數,寫出來會更復雜。好比,要自定義log的文本:
def log(text): def decorator(func): def wrapper(*args,**kwargs): print('%s %s():' % (text,func.__name__)) return func(*args,**kwargs) return wrapper return decorator @log('execute') def helloworld(): print('hello world') helloworld()
上面代碼的執行過程至關於helloworld = log('execute')(helloworld)
咱們講了函數也是對象,它有name等屬性,但你去看通過decorator裝飾以後的函數,它們的name已經從原來的'helloworld'變成了'wrapper':
helloworld.__name__ #'wrapper'
若是須要把原始函數的name等屬性複製到wrapper()函數中,使用Python內置的functools.wraps函數,代碼以下:
import functools def log(func): @functools.wraps(func) def wrapper(*args, **kw): print('call %s():' % func.__name__) return func(*args, **kw) return wrapper @log def helloworld(): print('hello world') helloworld.__name__
此時的輸出就變爲了'helloworld'
通常的函數有許多須要定義的參數,假設咱們想要固定其中的某些參數,返回一些新的函數,咱們就可使用functools.partial幫助咱們建立一個偏函數,從而使得調用變得簡單
import functools int2 = functools.partial(int, base=2) int2('10010') # 18
固然咱們也能夠穿入一個函數字典:
kw = { 'base': 2 } int('10010', **kw)
當傳入的參數沒有對應的key時,它默認時做爲*args的一部分自動加到左邊,所以下面的函數至關於比較max(10,5,6,7),返回10:
max2 = functools.partial(max, 10) max2(5, 6, 7)