這篇文章講述了Python進階:高階函數的詳細說明有需要的朋友可以參考
函數式程式設計
函數是Python內建支援的一種封裝,我們透過把大段程式碼拆成函數,透過一層一層的函數調用,就可以把複雜任務分解成簡單的任務,這種分解可以稱之為面向過程的程式設計。函數就是過程導向的程式設計的基本單元。
而函數式程式設計(請注意多了一個「式」字)-Functional Programming,雖然也可以歸結到過程導向的程式設計,但其想法更接近數學計算。
我們首先要搞清楚電腦(Computer)和計算(Compute)的概念。
在電腦的層次上,CPU執行的是加減乘除的指令碼,以及各種條件判斷和跳躍指令,所以,組合語言是最貼近電腦的語言。
而計算則指數學意義上的計算,越是抽象的計算,離電腦硬體越遠。
對應到程式語言,就是越低階的語言,越貼近計算機,抽象程度低,執行效率高,例如C語言;越高階的語言,越貼近計算,抽象程度高,執行效率低,例如Lisp語言。
函數式程式設計就是一種抽象程度很高的程式設計範式,純粹的函數式程式語言所寫的函數沒有變量,因此,任意一個函數,只要輸入是確定的,輸出就是確定的,這種純函數我們稱之為沒有副作用。而允許使用變數的程式設計語言,由於函數內部的變數狀態不確定,同樣的輸入,可能得到不同的輸出,因此,這種函數是有副作用的。
函數式程式設計的一個特點是,允許把函數本身當作參數傳入另一個函數,也允許傳回一個函數!
Python對函數式程式設計提供部分支援。由於Python允許使用變量,因此,Python不是純函數式程式語言。
高階函數
高階函數英文叫Higher-order function。什麼是高階函數?我們以實際程式碼為例子,一步一步深入概念。
變數可以指向函數
以Python內建的求絕對值的函數abs()為例,呼叫函數用以下程式碼:
>>> abs(-10)10
但是,如果只寫abs呢?
>>> abs
可見,abs(-10)是函數調用,而abs是函數本身。
要得到函數呼叫結果,我們可以把結果賦值給變數:
>>> x = abs(-10)>>> x10
但是,如果把函數本身賦值給變數呢?
>>> f = abs
>>> f
#結論:函數本身也可以賦值給變量,即:變數可以指向函數。
如果一個變數指向了一個函數,那麼,可否透過該變數來呼叫這個函數?用程式碼驗證一下:
>>> f = abs>>> f(-10)10
成功!說明變數f現在已經指向了abs函數本身。直接呼叫abs()函數和呼叫變數f()完全相同。
函數名稱也是變數
那麼函數名稱是什麼呢?函數名其實就是指向函數的變數!對於abs()這個函數,完全可以把函數名abs看成變量,它指向一個可以計算絕對值的函數!
如果把abs指向其他對象,會有什麼情況發生?
>>> abs = 10
>>> abs(-10)
Traceback (most recent call last):
File "
TypeError: 'int' object is not callable
把abs指向10後,就無法透過abs(-10)呼叫該函數了!因為abs這個變數已經不指向求絕對值函數而是指向一個整數10!
當然實際程式碼絕對不能這麼寫,這裡是為了說明函數名稱也是變數。若要恢復abs函數,請重新啟動Python互動環境。
註:由於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) ==> 11return 11
用程式碼驗證一下:
>>> add(-5, 6, abs)11
寫高階函數,就是讓函數的參數能夠接收別的函數。
小結
把函數當作參數傳入,這樣的函數稱為高階函數,函數式程式設計就是指這種高度抽象的程式設計範式。
map/reduce
Python內建了map()和reduce()函數。
如果你讀過Google的那篇大名鼎鼎的論文“MapReduce: Simplified Data Processing on Large Clusters”,你就能大概明白map/reduce的概念。
我們先看map。 map()函數接收兩個參數,一個是函數,一個是Iterable,map將傳入的函數依序作用到序列的每個元素,並將結果傳回作為新的Iterator。
舉例說明,例如我們有一個函數f(x)=x2,要把這個函數作用在一個list [1, 2, 3, 4, 5, 6, 7, 8, 9]上,就可以用map()實作如下:
現在,我們用Python程式碼實作如下:
現在,我們用Python程式碼實作:
>>> def f(x): ... return x * x
...>>> r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])>>> list(r)[1, 4, 9, 16, 25, 36, 49, 64, 81]
map()傳入的第一個參數是f,即函數對象本身。由於結果r是一個Iterator,Iterator是惰性序列,因此透過list()函數讓它把整個序列都計算出來並傳回一個list。
你可能會想,不需要map()函數,寫一個循環,也可以計算出結果:
L = []for n in [1, 2, 3, 4, 5, 6, 7, 8, 9]:
L.append(f(n))print(L)
的確可以,但是,從上面的循環程式碼,能一眼看懂「把f(x)作用在list的每一個元素並把結果生成一個新的list」嗎?
所以,map()作為高階函數,事實上它把運算規則抽象化了,因此,我們不但可以計算簡單的f(x)=x2,還可以計算任意複雜的函數,比如,把這個list所有數字轉為字串:
>>> list(map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9]))[ '1', '2', '3', '4', '5', '6', '7', '8', '9']
只需要一行程式碼。
再看reduce的用法。 reduce把一個函數作用在一個序列[x1, x2, x3, ...]上,這個函數必須接收兩個參數,reduce把結果繼續和序列的下一個元素做累積計算,其效果就是:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
...> ;>> reduce(add, [1, 3, 5, 7, 9])25
當然求和運算可以直接用Python內建函數sum(),沒必要動用reduce 。
...>>> reduce(fn, [1, 3, 5, 7, 9]) 13579
這個例子本身沒多大用處,但是,如果考慮到字串str也是一個序列,對上面的例子稍加改動,配合map(),我們就可以寫出把str轉換為int的函數:
>>> from functools import reduce>>> def fn(x, y):... return x * 10 + y
...> ;>> def char2num(s):... return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]###...>>> reduce(fn, map(char2num, ' 13579'))13579#########整理成一個str2int的函數就是:###from functools import reducedef str2int(s): def fn(x, y): return x * 10 + y def char2num(s): 10 + y def char2num(s): turn'1', '1' 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s] return reduce( fn, map(char2num, s))
# list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))# 結果: [1, 5, 9, 15]
list(filter(not_empty, ['A', '' , 'B', None, 'C', ' ']))# 結果: ['A', 'B', 'C']
[5, 9, -12, -21, 36 ]
| | 我
#>>> sorted(['bob', 'about', 'Zoo', 'Credit'])['Credit', 'Zoo', 'about', 'bob']
這樣,我們給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']
從上述例子可以看出,高階函數的抽象能力是非常強大的,而且,核心程式碼可以保持得非常簡潔。
小結
sorted()也是一個高階函數。用sorted()排序的關鍵在於實作一個映射函數。
傳回函數
函數當作傳回值
高階函數除了可以接受函數當參數外,還可以把函數傳回作為結果值。
我們來實作一個可變參數的求和。通常情況下,求和的函數是這樣定義的:
def calc_sum(*args):
ax = 0 for n in args:
= ax + n return ax
ax = 0 for n in args:
ax. ax + n return ax return sum
>>> f
##。當函數f時,才真正計算求和的結果:
>>> f()25
>>> f1 = lazy_sum(1, 3, 5, 7, 9)>>> f2 = lazy_sum(1, 3, 5, 7, 9)>>> f1==f2False
匿名函數有個限制,就是只能有一個表達式,不用寫return,回傳值就是該表達式的結果。
用匿名函數有個好處,因為函數沒有名字,所以不必擔心函數名稱衝突。此外,匿名函數也是函數對象,也可以把匿名函數賦值給一個變量,再利用變數來呼叫函數:
>>> f = lambda x: x * x
> ;>> f
>>> f(5)
25
一樣,也可以把匿名函數以回傳值傳回,例如:
def build(x, y): return lambda: x * x + y * y
小結
Python對匿名函數的支援有限,只有一些簡單的情況下可以使用匿名函數。
偏函數
Python的functools模組提供了許多有用的功能,其中一個就是偏函數(Partial function)。要注意,這裡的偏函數和數學意義上的偏函數不一樣。
在介紹函數參數的時候,我們講到,透過設定參數的預設值,可以降低函數呼叫的難度。而偏函數也可以做到這一點。舉例如下:
int()函數可以把字串轉換為整數,當只傳入字串時,int()函數預設會以十進位轉換:
>>> int('12345')12345
但int()函數也提供額外的base參數,預設值為10。如果傳入base參數,就可以做N進位的轉換:
>>> int('12345', base=8)5349
>>> int(' 12345', 16)74565
假設要轉換大量的二進位字串,每次都傳入int(x, base=2)非常麻煩,於是,我們想到,可以定義一個int2( )的函數,預設把base=2傳進去:
def int2(x, base=2): return int(x, base)
#這樣,我們轉換二進位就非常方便了:
>>> int2('1000000')64>>> int2('1010101')85
functools.partial就是幫助我們建立一個偏函數的,不需要我們自己定義int2(),可以直接用下面的程式碼建立一個新的函數int2:
>>> import functools>>> int2 = functools .partial(int, base=2)>>> int2('1000000')64>>> int2('1010101')85
所以,簡單總結functools.partial的作用就是,把一個函數的某些參數給固定住(也就是設定預設值),回傳一個新的函數,呼叫這個新函數會比較簡單。
注意到上面的新的int2函數,只是把base參數重新設定預設值為2,但也可以在函數呼叫時傳入其他值:
>> > int2('1000000', base=10)1000000
最後,建立偏函數時,實際上可以接收函數物件、*args和**kw這3個參數,當傳入:
int2 = functools.partial(int, base=2)
#其實固定了int()函數的關鍵字參數base,也就是:
int2('10010')
相當於:
kw = { 'base': 2 }int('10010', **kw)
#當傳入:
max2 = functools.partial(max, 10)
實際上會把10當作*args的一部分自動加到左邊,也就是:
max2(5, 6, 7)
相當於:
args = (10, 5, 6, 7)
max(*args )
結果為10。
小結
當函數的參數數量太多,需要簡化時,使用functools.partial可以建立一個新的函數,這個新函數可以固定住原函數的部分參數,從而在調用時更簡單。
以上是Python進階:高階函數的詳細說明的詳細內容。更多資訊請關注PHP中文網其他相關文章!

Python和C 各有優勢,選擇應基於項目需求。 1)Python適合快速開發和數據處理,因其簡潔語法和動態類型。 2)C 適用於高性能和系統編程,因其靜態類型和手動內存管理。

選擇Python還是C 取決於項目需求:1)如果需要快速開發、數據處理和原型設計,選擇Python;2)如果需要高性能、低延遲和接近硬件的控制,選擇C 。

通過每天投入2小時的Python學習,可以有效提升編程技能。 1.學習新知識:閱讀文檔或觀看教程。 2.實踐:編寫代碼和完成練習。 3.複習:鞏固所學內容。 4.項目實踐:應用所學於實際項目中。這樣的結構化學習計劃能幫助你係統掌握Python並實現職業目標。

在兩小時內高效學習Python的方法包括:1.回顧基礎知識,確保熟悉Python的安裝和基本語法;2.理解Python的核心概念,如變量、列表、函數等;3.通過使用示例掌握基本和高級用法;4.學習常見錯誤與調試技巧;5.應用性能優化與最佳實踐,如使用列表推導式和遵循PEP8風格指南。

Python適合初學者和數據科學,C 適用於系統編程和遊戲開發。 1.Python簡潔易用,適用於數據科學和Web開發。 2.C 提供高性能和控制力,適用於遊戲開發和系統編程。選擇應基於項目需求和個人興趣。

Python更適合數據科學和快速開發,C 更適合高性能和系統編程。 1.Python語法簡潔,易於學習,適用於數據處理和科學計算。 2.C 語法複雜,但性能優越,常用於遊戲開發和系統編程。

每天投入兩小時學習Python是可行的。 1.學習新知識:用一小時學習新概念,如列表和字典。 2.實踐和練習:用一小時進行編程練習,如編寫小程序。通過合理規劃和堅持不懈,你可以在短時間內掌握Python的核心概念。

Python更易學且易用,C 則更強大但複雜。 1.Python語法簡潔,適合初學者,動態類型和自動內存管理使其易用,但可能導致運行時錯誤。 2.C 提供低級控制和高級特性,適合高性能應用,但學習門檻高,需手動管理內存和類型安全。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

ZendStudio 13.5.1 Mac
強大的PHP整合開發環境

SAP NetWeaver Server Adapter for Eclipse
將Eclipse與SAP NetWeaver應用伺服器整合。

DVWA
Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

VSCode Windows 64位元 下載
微軟推出的免費、功能強大的一款IDE編輯器

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)