Python中高階函數(shù)的小實踐分享
1. filter
1.1 把一個序列中的空字符串刪掉
例如將[‘A’, ‘’, ‘B’, None, ‘C’, ’ ']返回為[‘A’, ‘B’, ‘C’]
def not_empty(s): return s and s.strip() list(filter(not_empty, ['A', '', 'B', None, 'C', ' ']))
可見用filter()這個高階函數(shù),關(guān)鍵在于正確實現(xiàn)一個“篩選”函數(shù)。
注意到filter()函數(shù)返回的是一個Iterator,也就是一個惰性序列,所以要強迫filter()完成計算結(jié)果,需要用list()函數(shù)獲得所有結(jié)果并返回list。
1.2 用filter求素數(shù)
計算素數(shù)的一個方法是埃氏篩法,它的算法理解起來非常簡單:
首先,列出從2開始的所有自然數(shù),構(gòu)造一個序列:
2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, …
取序列的第一個數(shù)2,它一定是素數(shù),然后用2把序列的2的倍數(shù)篩掉:
3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, …
取新序列的第一個數(shù)3,它一定是素數(shù),然后用3把序列的3的倍數(shù)篩掉:
5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, …
取新序列的第一個數(shù)5,然后用5把序列的5的倍數(shù)篩掉:
7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, …
不斷篩下去,就可以得到所有的素數(shù)。
用Python來實現(xiàn)這個算法,可以先構(gòu)造一個從3開始的奇數(shù)序列:
def _odd_iter(): n = 1 while True: n = n + 2 yield n
注意這是一個生成器,并且是一個無限序列。
然后定義一個篩選函數(shù):
def _not_divisible(n): return lambda x: x % n > 0
最后,定義一個生成器,不斷返回下一個素數(shù):
def primes(): yield 2 it = _odd_iter() # 初始序列 while True: n = next(it) # 返回序列的第一個數(shù) yield n it = filter(_not_divisible(n), it) # 構(gòu)造新序列
這個生成器先返回第一個素數(shù)2,然后,利用filter()不斷產(chǎn)生篩選后的新的序列。
由于primes()也是一個無限序列,所以調(diào)用時需要設(shè)置一個退出循環(huán)的條件:
# 打印1000以內(nèi)的素數(shù): for n in primes(): if n < 1000: print(n) else: break
注意到Iterator是惰性計算的序列,所以我們可以用Python表示“全體自然數(shù)”,“全體素數(shù)”這樣的序列,而代碼非常簡潔。
2. map(), reduce()
Python內(nèi)建了map()和reduce()函數(shù)。
如果你讀過Google的那篇大名鼎鼎的論文“MapReduce: Simplified Data Processing on Large Clusters”,你就能大概明白map/reduce的概念。
我們先看map。map()函數(shù)接收兩個參數(shù),一個是函數(shù),一個是Iterable,map將傳入的函數(shù)依次作用到序列的每個元素,并把結(jié)果作為新的Iterator返回。
舉例說明,比如我們有一個函數(shù)f(x)=x2,要把這個函數(shù)作用在一個list [1, 2, 3, 4, 5, 6, 7, 8, 9]上,就可以用map()實現(xiàn)如下:
f(x) = x * x
│
│
┌───┬───┬───┬───┼───┬───┬───┬───┐
│ │ │ │ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼
[ 1 2 3 4 5 6 7 8 9 ]
│ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼
[ 1 4 9 16 25 36 49 64 81 ]
現(xiàn)在,我們用Python代碼實現(xiàn):
def f(x): return x * x r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9]) print(list(r))
map()傳入的第一個參數(shù)是f,即函數(shù)對象本身。由于結(jié)果r是一個Iterator,Iterator是惰性序列,因此通過list()函數(shù)讓它把整個序列都計算出來并返回一個list。
你可能會想,不需要map()函數(shù),寫一個循環(huán),也可以計算出結(jié)果:
L = [] for n in [1, 2, 3, 4, 5, 6, 7, 8, 9]: L.append(f(n)) print(L)
的確可以,但是,從上面的循環(huán)代碼,能一眼看明白“把f(x)作用在list的每一個元素并把結(jié)果生成一個新的list”嗎?
所以,map()作為高階函數(shù),事實上它把運算規(guī)則抽象了,因此,我們不但可以計算簡單的f(x)=x2,還可以計算任意復(fù)雜的函數(shù),比如,把這個list所有數(shù)字轉(zhuǎn)為字符串:
list(map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9])) # ['1', '2', '3', '4', '5', '6', '7', '8', '9']
只需要一行代碼。
再看reduce的用法。reduce把一個函數(shù)作用在一個序列[x1, x2, x3, ...]上,這個函數(shù)必須接收兩個參數(shù),reduce把結(jié)果繼續(xù)和序列的下一個元素做累積計算,其效果就是:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
比方說對一個序列求和,就可以用reduce實現(xiàn):
from functools import reduce def add(x, y): return x + y reduce(add, [1, 3, 5, 7, 9])
當(dāng)然求和運算可以直接用Python內(nèi)建函數(shù)sum(),沒必要動用reduce。
但是如果要把序列[1, 3, 5, 7, 9]變換成整數(shù)13579,reduce就可以派上用場:
from functools import reduce def fn(x, y): return x * 10 + y reduce(fn, [1, 3, 5, 7, 9])
這個例子本身沒多大用處,但是,如果考慮到字符串str也是一個序列,對上面的例子稍加改動,配合map(),我們就可以寫出把str轉(zhuǎn)換為int的函數(shù):
from functools import reduce def fn(x, y): return x * 10 + y def char2num(s): digits = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9} return digits[s] reduce(fn, map(char2num, '13579'))
整理成一個str2int的函數(shù)就是:
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)) print(str2int('12306'))
還可以用lambda函數(shù)進(jìn)一步簡化成:
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 char2num(s): return DIGITS[s] def str2int(s): return reduce(lambda x, y: x * 10 + y, map(char2num, s))
也就是說,假設(shè)Python沒有提供int()函數(shù),你完全可以自己寫一個把字符串轉(zhuǎn)化為整數(shù)的函數(shù),而且只需要幾行代碼!
lambda函數(shù)的用法在后面介紹。
3. sorted()
排序也是在程序中經(jīng)常用到的算法。無論使用冒泡排序還是快速排序,排序的核心是比較兩個元素的大小。如果是數(shù)字,我們可以直接比較,但如果是字符串或者兩個dict呢?直接比較數(shù)學(xué)上的大小是沒有意義的,因此,比較的過程必須通過函數(shù)抽象出來。
Python內(nèi)置的sorted()函數(shù)就可以對list進(jìn)行排序:
sorted([36, 5, -12, 9, -21])
此外,sorted()函數(shù)也是一個高階函數(shù),它還可以接收一個key函數(shù)來實現(xiàn)自定義的排序,例如按絕對值大小排序:
sorted([36, 5, -12, 9, -21], key=abs)
key指定的函數(shù)將作用于list的每一個元素上,并根據(jù)key函數(shù)返回的結(jié)果進(jìn)行排序。對比原始的list和經(jīng)過key=abs處理過的list:
list = [36, 5, -12, 9, -21] keys = [36, 5, 12, 9, 21]
然后sorted()函數(shù)按照keys進(jìn)行排序,并按照對應(yīng)關(guān)系返回list相應(yīng)的元素:
keys排序結(jié)果 => [5, 9, 12, 21, 36]
最終結(jié)果 => [5, 9, -12, -21, 36]
我們再看一個字符串排序的例子:
>>> sorted(['bob', 'about', 'Zoo', 'Credit']) ['Credit', 'Zoo', 'about', 'bob']
默認(rèn)情況下,對字符串排序,是按照ASCII的大小比較的,由于'Z' < 'a',結(jié)果,大寫字母Z會排在小寫字母a的前面。
現(xiàn)在,我們提出排序應(yīng)該忽略大小寫,按照字母序排序。要實現(xiàn)這個算法,不必對現(xiàn)有代碼大加改動,只要我們能用一個key函數(shù)把字符串映射為忽略大小寫排序即可。忽略大小寫來比較兩個字符串,實際上就是先把字符串都變成大寫(或者都變成小寫),再比較。
這樣,我們給sorted傳入key函數(shù),即可實現(xiàn)忽略大小寫的排序:
sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower) ['about', 'bob', 'Credit', 'Zoo']
要進(jìn)行反向排序,不必改動key函數(shù),可以傳入第三個參數(shù)reverse=True:
>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True) ['Zoo', 'Credit', 'bob', 'about']
從上述例子可以看出,高階函數(shù)的抽象能力是非常強大的,而且,核心代碼可以保持得非常簡潔。
小結(jié)
sorted()也是一個高階函數(shù)。用sorted()排序的關(guān)鍵在于實現(xiàn)一個映射函數(shù)。
以上就是Python中高階函數(shù)的小實踐分享的詳細(xì)內(nèi)容,更多關(guān)于Python高階函數(shù)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
python爬蟲開發(fā)之urllib模塊詳細(xì)使用方法與實例全解
這篇文章主要介紹了python爬蟲開發(fā)之urllib模塊詳細(xì)使用方法與實例全解,需要的朋友可以參考下2020-03-03python中isdigit() isalpha()用于判斷字符串的類型問題
這篇文章主要介紹了python中isdigit() isalpha()用于判斷字符串的類型問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-11-11使用python進(jìn)行文本預(yù)處理和提取特征的實例
今天小編就為大家分享一篇使用python進(jìn)行文本預(yù)處理和提取特征的實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-06-06Python數(shù)據(jù)持久化存儲實現(xiàn)方法分析
這篇文章主要介紹了Python數(shù)據(jù)持久化存儲實現(xiàn)方法,結(jié)合實例形式分析了Python基于pymongo及mysql模塊的數(shù)據(jù)持久化存儲操作相關(guān)實現(xiàn)技巧,需要的朋友可以參考下2019-12-12Python?日期和時間處理教程之datetime?模塊的使用示例詳解
Python 中的日期不是獨立的數(shù)據(jù)類型,但我們可以導(dǎo)入一個名為 datetime 的模塊來使用日期作為日期對象,這篇文章主要介紹了Python?日期和時間處理教程:datetime?模塊的使用,需要的朋友可以參考下2023-10-10