Python的高階函數小結

一. 高階函數定義

簡而言之,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教程--高階函數

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章