python資料結構之匿名函數lambda

2022-01-05 18:00:01

🍊前面的章節,我們學習完了python的基本資料結構:基本資料型別、棧、佇列、連結串列、遞迴、排序、搜尋、樹等,今天我們來學習匿名函數lambda,對往期內容感興趣的同學可以參考👇:

🌰今天所要學習的匿名函數lambda是宣告函數的另一種方式,可以將簡單的函數用一行程式碼來表示,或許是因為lambda所宣告的函數沒有名字,所以lambda被稱為匿名函數(沒有名字的函數)

1. lambda的介紹

通常情況下,我們使用def語句來定義函數,但python還提供了一種生成函數物件的表示式形式。由於它與LISP語言中的一個工具很相似,所以稱為lambda。就像def一樣,這個表示式建立了一個之後能夠呼叫的函數,但是它返回了一個函數而不是將這個函數賦值給一個變數名,它們常常以一種行內進行函數定義的形式使用,或者用作推遲執行一些程式碼。

2.lambda表示式

lambda的一般形式是關鍵字lambda,之後是一個或多個引數(與一個def頭部內用括號括起來的參數列極其相似),緊跟的是一個冒號,之後是一個表示式:

lambda argument1,argument2,argument3,...,argumentN:expression using arguments

2.1 lambda和def的差別

由lambda表示式所返回的函數物件與由def建立並賦值後的函數物件工作起來是完全一樣的,但是lambda有場合比def顯得更方便,更簡潔。

  • lambda是一個表示式,而不是一個語句。因為這一點,lambda能夠出現在Python語法不允許def出現的地方——例如,在一個列表常數中或者函數呼叫的引數中。此外,作為一個表示式,lambda返回了一個值(一個新的函數),可以選擇性地賦值給一個變數名。相反,def語句總是得在頭部將一個新的函數賦值給一個變數名,而不是將這個函數作為結果返回。
  • lambda的主體是一個單個的表示式,而不是一個程式碼塊。這個lambda的主體簡單得就好像放在def主體的return語句中的程式碼一樣。簡單地將結果寫成一個順暢的表示式,而不是明確的返回。因為它僅限於表示式,lambda通常要比def功能要小:你僅能夠在lambda主體中封裝有限的邏輯進去,連if這樣的語句都不能夠使用。這是有意設計的——它限制了程式的巢狀:lambda是一個為編寫簡單的函數而設計的,而def用來處理更大的任務。

總而言之,lambda起到了一種函數速寫的作用,允許在使用的程式碼內嵌入一個函數的定義。它們完全是可選的(你總是能夠使用def來替代它們),但是在你僅需要嵌入小段可執行程式碼的情況下它們會帶來一個更簡潔的程式碼結構。

2.2 lambda使用方式

我們使用lambda來建立函數,看一下lambda宣告函數的方式:

#用def宣告一個函數
def fun1(x,y,z):
    return x*y*z
fun1(1,2,3)
#用lambda宣告一個函數
f1=lambda x,y,z:x*y*z 
f1(1,2,3)

這裡的f1被賦值給一個lambda表示式建立的函數物件。這也就是def所完成的任務,只不過def的賦值是自動進行的。

f2=lambda x='he',y='ll',z='o':x+y+z 

結果如下:
在這裡插入圖片描述
這說明lambda也可以使用預設的引數,就像在def中設定預設引數一樣。

在一些場合中,比如列表,字典中嵌入函數,但def不會在這些列表或字典中執行,這時候就需要用到lambda

#使用lambda構造一個函數列表
l1=[lambda x:x**2,lambda x:x**3,lambda x:x**4]
for i in l1:#列印結果
    print(i(2))

結果如下:
在這裡插入圖片描述
當然l1也可以當作正常列表切片操作:
在這裡插入圖片描述
而def實現該功能,需要在列表外定義三個函數,顯得很繁瑣:

#需要def三個函數
def f1(x):return x**2
def f2(x):return x**3
def f3(x):return x**4
l2=[f1,f2,f3]
for j in l2:
    print(j(2))

傳入字典中也是同一樣的道理:

#字典內新增lambda
dict={"name":(lambda x: x-5),"page":(lambda x:x**2),"years":(lambda x:x/2)}
#進行切片操作
dict['name'](10)

結果如下:
在這裡插入圖片描述

2.3 避免使用巢狀

lambda也可以這樣使用:

(lambda x:(lambda y:x+y)(87))(13)

結果如下:
在這裡插入圖片描述
這種巢狀的結構,巢狀的lambda程式碼都能夠獲取在上層lambda函數中的變數x,但是這種結構可讀性很差,很令人費解,儘量不要使用這種結構。

3. map函數使用方法

map呼叫與列表解析很相似,但是map對每一個元素都應用了函數呼叫而不是任意的表示式,例如:我們經常對列表和其他序列常常要做的一件事就是對每一個元素進行一個操作並把其結果集合起來。例如,在一個列表counter中更新所有的數位,可以簡單地通過一個for迴圈來實現。

#每個元素加10
a=[1,2,3,4]
b=[]
for i in a:
    b.append(i+10)
print(b)

使用map函數會對一個序列物件中的每一個元素應用被傳入的函數,並且返回一個包含了所有函數呼叫結果的一個列表。

#map實現元素加10 第一種方法
def f(x):
    return x+10
list(map(f,a))

##map實現元素加10 第二種方法
list(map(lambda x:x+10,a))

結果如下:
在這裡插入圖片描述
因為map是內建函數,它總是可用的,並總是以同樣的方式工作,還有一些效能方面的優勢,簡而言之,它要比自己編寫的for迴圈更快。此外map還可以提供多個序列作為引數:

#求1^1,2^2,3^3次方
list(map(pow,[1,2,3],[1,2,3]))

結果如下:
在這裡插入圖片描述

4. filter和reduce函數使用方法

filter函數主要作用是基於某一測試函數過濾出一些元素。

#在陣列中挑出大於0的元素
a=range(-5,5)
list(filter(lambda x:x>0,a))

結果如下:
在這裡插入圖片描述序列中的元素若其返回值為真的話,將會被鍵入到結果的列表中。

reduce在Python 2.6中只是一個簡單的內建函數,但是在Python 3.0中則位於functools模組中,需要導包

from functools import reduce
a=[1,2,3,4]
#計算所有序列之和
reduce(lambda x,y:x+y,a)
#計算所有序列之積
reduce(lambda x,y:x*y,a)

結果如下:
在這裡插入圖片描述
每一步,reduce傳遞了當前的和或乘積以及列表中下一個的元素,傳給列出的lambda函數。預設,序列中的第一個元素初始化了起始值。

5.總結

本章節我們主要講述了lambda函數、map函數、filter函數、reduce函數,其中lambda表示式函數可以高效的幫助我們完成一些序列操作的任務,而map,filter,reduce配合lambda使用可以發揮出他們強大的功能,而且這些函數在分散式計算中也有著重要意義。

6.參考資料

《python資料結構與演演算法》
《大話資料結構》
《python學習手冊》