本篇文章咱們來介紹下Python函數式編程的知識。最主要的一點,Python中的函數是對象,能夠複製給變量!好了,咱們來介紹幾個Python函數式編程中的要點,包括高階函數、匿名函數、裝飾器、偏函數等等。精彩內容,不容錯過!編程
Python函數式編程-高階函數、匿名函數、裝飾器、偏函數
顯示在電腦屏幕上的計算機程序源代碼,可用做網絡科技等素材或背景。
若是你在學習Python的過程當中碰見了不少疑問和難題,能夠加-q-u-n 227 -435-450裏面有軟件視頻資料免費
一、高階函數網絡
函數自己也能夠賦值給變量,即:變量能夠指向函數。若是一個變量指向了一個函數,那麼,能夠經過該變量來調用這個函數。閉包
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函數視頻
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
當咱們在傳入函數時,有些時候,不須要顯式地定義函數,直接傳入匿名函數更方便。相信你們對於匿名函數必定不陌生,其實就是咱們常說的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)