什麼是高階函數?把函數作為參數傳入,這樣的函數稱為高階函數,函數式編程就是指這種高度抽象的編程範式。 在前面的章節中,我們知道可以用abs()這個函數來得到一個數的絕對值,如: 以上代碼,輸出: 如果,我們把代碼修改下,把abs賦值給一個變數: 以上代碼,輸出: 可見,abs(-100)是函數調用, ...
什麼是高階函數?把函數作為參數傳入,這樣的函數稱為高階函數,函數式編程就是指這種高度抽象的編程範式。
在前面的章節中,我們知道可以用abs()這個函數來得到一個數的絕對值,如:
print('abs(-100):', abs(-100))
以上代碼,輸出:
abs(-100): 100
如果,我們把代碼修改下,把abs賦值給一個變數:
f = abs print(f(-100))
以上代碼,輸出:
100
可見,abs(-100)是函數調用,而abs是函數本身,要獲得函數調用結果,我們可以把結果賦值給變數。函數本身也可以賦值給變數,即:變數可以指向函數,這時變數就獲得了函數的功能,如上例中的 f ,直接調用f()和直接調用abs()完全相同。
那麼函數名是什麼呢?函數名其實就是指向函數的變數!對於abs()這個函數,完全可以把函數名abs看成變數,它指向一個可以計算絕對值的函數!
如果把abs指向其他對象,會有什麼情況發生?
abs = 10
print(abs(-10))
把abs指向10後,就無法通過abs(-10)調用該函數了!因為abs這個變數已經不指向求絕對值函數而是指向一個整數10!當然實際代碼絕對不能這麼寫,這裡是為了說明函數名也是變數。
註:由於abs函數實際上是定義在import builtins模塊中的,所以要讓修改abs變數的指向在其它模塊也生效,要用import builtins; builtins.abs = 10。關於什麼是模塊,後面會講到,這裡不要去糾結。
既然變數可以指向函數,函數的參數能接收變數,那麼一個函數就可以接收另一個函數作為參數,這種函數就稱之為高階函數。
一個最簡單的高階函數:
def add(x, y, f): return f(x) + f(y)
當我們調用add(-5, 6, abs)時,參數x,y和f分別接收-5,6和abs,根據函數定義,我們可以推導計算過程為:
x = -5 y = 6 f = abs f(x) + f(y) ==> abs(-5) + abs(6) ==> 11 return 11
用代碼驗證下:
def add(x, y, f): return f(x) + f(y) print(add(-5, 6, abs))
1、map函數
這是Python內置的一個函數,map()函數接收兩個參數,一個是函數,一個是Iterable(迭代器對象),map將傳入的函數依次作用到序列的每個元素,並把結果作為新的Iterator(迭代器)返回:
def f(x): """ 返回一個數的3次方 :param x: 數 :return: 3次方後的結果 """ return x**3 r = map(f, [1, 2, 3, 4, 5]) print(list(r))
以上代碼,輸出:
[1, 8, 27, 64, 125]
map()傳入的第一個參數是f,即函數對象本身。由於結果r是一個Iterator,Iterator是惰性序列,因此通過list()函數讓它把整個序列都計算出來並返回一個list。
當然,不需要map()函數,寫一個迴圈,也可以計算出結果:
def f(x): """ 返回一個數的3次方 :param x: 數 :return: 3次方後的結果 """ return x**3 L = [] for n in [1, 2, 3, 4, 5]: L.append(f(n)) print(L)
執行後,結果是一樣的,但這樣的寫法,我們並不能直觀的知道f()是作用在列表的所有元素上,並返回一個列表,我們必須讀了源代碼後才知道。
所以,map()作為高階函數,事實上它把運算規則抽象了,因此,我們不但可以計算簡單的f(x)=x3,還可以計算任意複雜的函數,比如,把這個list所有數字轉為字元串:
print(list(map(str, [1, 2, 3, 4, 5])))
以上代碼,輸出:
['1', '2', '3', '4', '5']
當然,更多的時候我們也會配合lambda來使用,如:
r = map(lambda x: x**3, [1, 2, 3, 4, 5]) print(list(r))
以上代碼,輸出:
[1, 8, 27, 64, 125]
2、reduce函數
reduce把一個函數作用在一個序列[x1, x2, x3, ...]上,這個函數必須接收兩個參數,reduce把結果繼續和序列的下一個元素做累積計算,其效果就是:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
對一個序列求和,就可以用reduce實現:
from functools import reduce def add(x, y): return x + y print('reduce(add, [1, 3, 5, 7, 9]) = ', reduce(add, [1, 3, 5, 7, 9]))
以上代碼,輸出:
reduce(add, [1, 3, 5, 7, 9]) = 25
當然求和運算可以直接用Python內建函數sum(),思考:如何把序列 [1, 2, 3, 4, 5] 轉化為整型的 12345 ,下麵給出代碼但請儘可能自己先寫出
from functools import reduce def f(x, y): return x*10 + y res = reduce(f, [1, 2, 3, 4, 5]) print(res)View Code
思考:假如python沒有提供 int() 函數,如何使用 map與reduce自己寫一個函數,實現將 '123456' 轉化為 123456 ,下麵給出代碼但請儘可能自己先寫出:
from functools import reduce def str2int(s): """ 將字元串轉為數字 :param s: 要轉化的字元串 :return: 數字 """ DIGITS = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9} def char2num(c): """ 字元轉數字 :param c:字元 :return: 數字 """ return DIGITS[c] def f(x, y): return x*10 + y resList = map(char2num, s) # 將字元串轉為列表 return reduce(f, resList) # 將列表元素依次排列組成數字,並返回 print(str2int('1234567'))View Code
3、filter函數
Python內建的filter()函數用於過濾序列。filter()接收一個函數和一個序列,把傳入的函數依次作用於每個元素,然後根據返回值是True還是False決定保留還是丟棄該元素。
例如,在一個list中,刪掉偶數,只保留奇數,可以這麼寫:
def is_odd(n): return n % 2 == 1 res = list(filter(is_odd, [1, 2, 3, 4, 5, 6, 7, 8])) print(res)
以上代碼,輸出:
[1, 3, 5, 7]
註:filter()函數返回的是一個Iterator,也就是一個惰性序列,filter()的作用是從一個序列中篩出符合條件的元素。由於filter()使用了惰性計算,所以只有在取filter()結果的時候,才會真正篩選並每次返回下一個篩出的元素。
4、sorted函數
排序也是在程式中經常用到的演算法。無論使用冒泡排序還是快速排序,排序的核心是比較兩個元素的大小。如果是數字,我們可以直接比較,但如果是字元串或者兩個dict呢?直接比較數學上的大小是沒有意義的,因此,比較的過程必須通過函數抽象出來。
Python內置的sorted()函數就可以對list進行排序:
print(sorted([36, 5, -12, 9, -21]))
以上代碼,輸出:
[-21, -12, 5, 9, 36]
它還可以接收一個key函數來實現自定義的排序,例如按絕對值大小排序:
print(sorted([36, 5, -12, 9, -21], key=abs))
以上代碼,輸出:
[5, 9, -12, -21, 36]
key指定的函數將作用於list的每一個元素上,並根據key函數返回的結果進行排序。
這個是數字,那字元串排序是怎麼樣的呢?我們來看實例:
print(sorted(['hello', 'world', 'roy', 'python', 'c++']))
以上代碼,輸出:
['c++', 'hello', 'python', 'roy', 'world']
預設情況下,對字元串排序,是按照ASCII的大小比較的,每個字元依次比較(兩個字元串比較,先比較第一個字元,當第一個字元分出大小時則結束比較且比較的結果即為第一個字元相比較的結果,當第一個字元相同時則比較第二個字元,第二個字元相同時則比較第三個,依次下去直到得出結果),由於'Z' < 'a',結果,大寫字母Z會排在小寫字母a的前面。
可以用 reverse=True 進行反向排序:
print(sorted([36, 5, -12, 9, -21], reverse=True)) print(sorted(['hello', 'world', 'roy', 'python', 'c++'], reverse=True))
以上代碼,輸出:
[36, 9, 5, -12, -21] ['world', 'roy', 'python', 'hello', 'c++']