一. 高階函數定義
簡而言之,Python的高階函數就是指一個函數作為參數傳遞給另外一個函數的用法。
舉一個最簡單的高階函數來說明:
>>> def add(x,y,f): return f(x) + f(y) >>> add(1,-2,abs) 3
可能會有同學問,直接return abs(x) + abs(y)不就完了么,何必這么麻煩。
我的理解是把函數作為參數傳遞,能夠使得編碼涉及上更具有靈活性,比如我們可以根據某些變量的不同,傳入不同的函數進去,這樣能使得代碼更簡潔更好懂;不需要再重新寫一大堆代碼。
舉個例子
>>> def area_circle(x): return 3.14*x*x >>> def area_square(x): return x*x >>> def area(x,p): return p(x) >>> area(2,area_circle) 12.56 >>> area(2,area_square) 4
在這個例子中,如果有了新的多邊形(比如梯形等),我們只需要添加新的多邊形的計算函數就可以,而函數def area永遠都不需要變。配合dict,能使得代碼更加的優雅。
>>> area_calculation={'circle':area_circle, 'square':area_square} >>> area_type = 'circle' >>> area(2,area_calculation[area_type]) 12.56
二. 幾個比較有用的高階函數 map/reduce, filter, sorted
map/reduce:
map/reduce的概念大家應該不是第一次接觸,這個概念用的最廣的地方應該就是分布式計算:將計算任務拆分給多個slave計算機,然后將計算結果匯總整合。其實說白了,map/reduce的概念的核心就在於:map是將任務拆分,然后將拆分后的任務分別計算。Reduce是將map得到的各個計算結果進行匯總。只要理解了這一層,但凡涉及到map/reduce的概念都可以迎刃而解。
- map()函數:接收兩個參數,一個是函數,一個是可迭代對象 Iterable Object(關於可迭代對象Iterable Object和迭代器Iterator的概念請參考我的上一篇文章,講得很清楚)。map()函數將傳入的函數依次作用於可迭代對象的每個元素,並把結果作為Iterator返回。
下面舉個例子:
>>> def f(x): return x*x >>> r = map(f,[1,2,3,4]) #對於Iterator,我們有三種方式可以訪問到元素: #方式一:用next()函數訪問 >>> next(r) 1 >>> next(r) 4 >>> next(r) 9 >>> next(r) 16 >>> next(r) Traceback (most recent call last): File "<pyshell#146>", line 1, in <module> next(r) StopIteration #方式二:利用for循環訪問 >>> r = map(f,[1,2,3,4]) >>> for i in r: print(i) 1 4 9 16 #方式三: 轉換為list列表 >>> r = map(f,[1,2,3,4]) >>> list(r) [1, 4, 9, 16]
- Reduce函數:同樣的接收兩個參數,一個是函數,一個是可迭代對象 Iterable Object(eg: list列表)。reduce中的函數必須也要接收2個參數,執行時把前一個結果繼續和序列的下一個元素做累積計算,其效果就是:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
舉一個序列成數的例子(把序列[1,3,5,7,9變成13579])
>>> from functools import reduce >>> def fn(x, y): ... return x * 10 + y ... >>> reduce(fn, [1, 3, 5, 7, 9]) 13579
- Map/Reduce常常一起配合使用,下面的例子是一個用Map/Reduce把str轉換為int的函數:
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 str2int(s): def fn(x, y): return x * 10 + y def char2num(s): return DIGITS[s] return reduce(fn, map(char2num, s))
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]
注意到filter()
函數返回的是一個Iterator
,也就是一個惰性序列,所以要強迫filter()
完成計算結果,需要用list()
函數獲得所有結果並返回list。
Sorted:
排序是在程序中經常用到的算法。無論使用冒泡排序還是快速排序,排序的核心是比較兩個元素的大小。如果是數字,我們可以直接比較,但如果是字符串或者兩個dict呢?直接比較數學上的大小是沒有意義的,因此,比較的過程必須通過函數抽象出來。
Python內置的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]
key指定的函數將作用於list的每一個元素上,並根據key函數返回的結果進行排序。
我們再看一個字符串排序的例子:
>>> sorted(['bob', 'about', 'Zoo', 'Credit']) ['Credit', 'Zoo', 'about', 'bob']
默認情況下,對字符串排序,是按照ASCII的大小比較的,由於'Z' < 'a'
,結果,大寫字母Z
會排在小寫字母a
的前面。
現在,我們提出排序應該忽略大小寫,按照字母序排序。要實現這個算法,不必對現有代碼大加改動,只要我們能用一個key函數把字符串映射為忽略大小寫排序即可。忽略大小寫來比較兩個字符串,實際上就是先把字符串都變成大寫(或者都變成小寫),再比較。
這樣,我們給sorted
傳入key函數,即可實現忽略大小寫的排序:
>>> 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']
從上述例子可以看出,高階函數的抽象能力是非常強大的,而且,核心代碼可以保持得非常簡潔。
參考鏈接: 廖雪峰Python教程--高階函數