Python中常用最神祕的函式! lambda 函式深度總結!

語言: CN / TW / HK

今天我們來學習 Python 中的 lambda 函式,並探討使用它的優點和侷限性

Let's do it!

什麼是 Python 中的 Lambda 函式

lambda 函式是一個匿名函式(即,沒有名稱定義),它可以接受任意數量的引數,但與普通函式不同,它只計算並返回一個表示式

Python 中的 lambda 函式使用以下語法表達:

lambda 引數:表示式

lambda 函式包括三個元素:

  • 關鍵字 lambda:與普通函式中 def 類似
  • 引數:支援傳遞位置和關鍵字引數,與普通函式一樣
  • 正文:處理定引數的表示式

需要注意的是,普通函式不同,這裡不需要用括號將 lambda 函式的引數括起來,如果 lambda 函式有兩個或更多引數,我們用逗號列出它們

我們使用 lambda 函式只計算一個短表示式(理想情況下,單行)並且只計算一次,這意味著我們以後不會再複用這個函式。通常來說我們會將 lambda 函式作為引數傳遞給高階函式(接受其他函式作為引數的函式),例如 Python 內建函式,如 filter()、map() 或 reduce()等

Python 中的 Lambda 函式如何工作

讓我們看一個簡單的 lambda 函式示例:

lambda x: x + 1

Output:

<function __main__.<lambda>(x)>

上面的 lambda 函式接受一個引數,將其遞增 1,然後返回結果

它是以下帶有 def 和 return 關鍵字的普通函式的更簡單版本:

def increment_by_one(x):
    return x + 1

到目前我們的 lambda 函式 lambda x: x + 1 只建立一個函式物件,不返回任何內容,這是因為我們沒有為其引數 x 提供任何值(引數)。讓我們先分配一個變數,將它傳遞給 lambda 函式,看看這次我們得到了什麼:

a = 2
print(lambda x: a + 1)

Output:

<function <lambda> at 0x00000250CB0A5820>

我們的 lambda 函式沒有像我們預期的那樣返回 3,而是返回了函式物件本身及其記憶體位置,可以看出這不是呼叫 lambda 函式的正確方法。要將引數傳遞給 lambda 函式,執行它並返回結果,我們應該使用以下語法:

(lambda x: x + 1)(2)

Output:

3

雖然我們的 lambda 函式的引數沒有用括號括起來,但當我們呼叫它時,我們會在 lambda 函式的整個構造以及我們傳遞給它的引數周圍新增括號

上面程式碼中要注意的另一件事是,使用 lambda 函式,我們可以在建立函式後立即執行該函式並接收結果。這就是所謂的立即呼叫函式執行(或 IIFE)

我們可以建立一個帶有多個引數的 lambda 函式,在這種情況下,我們用逗號分隔函式定義中的引數。當我們執行這樣一個 lambda 函式時,我們以相同的順序列出相應的引數,並用逗號分隔它們:

(lambda x, y, z: x + y + z)(3, 8, 1)

Output:

12

也可以使用 lambda 函式來執行條件操作。下面是一個簡單 if-else 函式的 lambda 模擬:

print((lambda x: x if(x > 10) else 10)(5))
print((lambda x: x if(x > 10) else 10)(12))

Output:

如果存在多個條件(if-elif-...-else),我們必須巢狀它們:

(lambda x: x * 10 if x > 10 else (x * 5 if x < 5 else x))(11)

Output:

110

但是上面的寫法,又令程式碼變得難以閱讀

在這種情況下,具有 if-elif-...-else 條件集的普通函式將是比 lambda 函式更好的選擇。實際上,我們可以通過以下方式編寫上面示例中的 lambda 函式:

def check_conditions(x):
    if x > 10:
        return x * 10
    elif x < 5:
        return x * 5
    else:
        return x

check_conditions(11)

Output:

110

儘管上面的函式比相應的 lambda 函式增加了更多行,但它更容易閱讀

我們可以將 lambda 函式分配給一個變數,然後將該變數作為普通函式呼叫:

increment = lambda x: x + 1
increment(2)

Output:

3

但是根據 Python 程式碼的 PEP 8 樣式規則,這是一種不好的做法

賦值語句的使用消除了 lambda 表示式相對於顯式 def 語句所能提供的唯一好處(即,它可以嵌入到更大的表示式中)

因此如果我們確實需要儲存一個函式以供進一步使用,我們最好定義一個等效的普通函式,而不是將 lambda 函式分配給變數

Lambda 函式在 Python 中的應用

帶有 filter() 函式的 Lambda

Python 中的 filter() 函式需要兩個引數:

  • 定義過濾條件的函式
  • 函式在其上執行的可迭代物件

執行該函式,我們得到一個過濾器物件:

lst = [33, 3, 22, 2, 11, 1]
filter(lambda x: x > 10, lst)

Output:

<filter at 0x250cb090520>

為了從過濾器物件中獲取一個新的迭代器,並且原始迭代器中的所有項都滿足預定義的條件,我們需要將過濾器物件傳遞給 Python 標準庫的相應函式:list()、tuple()、set ()、frozenset() 或 sorted()(返回排序列表)

讓我們過濾一個數字列表,只選擇大於 10 的數字並返回一個按升序排序的列表:

lst = [33, 3, 22, 2, 11, 1]
sorted(filter(lambda x: x > 10, lst))

Output:

[11, 22, 33]
我們不必建立與原始物件相同型別的新可迭代物件,此外我們可以將此操作的結果儲存在一個變數中:

lst = [33, 3, 22, 2, 11, 1]
tpl = tuple(filter(lambda x: x > 10, lst))
tpl

Output:

(33, 22, 11)

帶有 map() 函式的 Lambda

我們使用 Python 中的 map() 函式對可迭代的每個專案執行特定操作。它的語法與 filter() 相同:一個要執行的函式和一個該函式適用的可迭代物件。

map() 函式返回一個 map 物件,我們可以通過將該物件傳遞給相應的 Python 函式來從中獲取一個新的迭代:list()、tuple()、set()、frozenset() 或 sorted()

與 filter() 函式一樣,我們可以從 map 物件中提取與原始型別不同型別的可迭代物件,並將其分配給變數。

下面是使用 map() 函式將列表中的每個專案乘以 10 並將對映值作為分配給變數 tpl 的元組輸出的示例:

lst = [1, 2, 3, 4, 5]
print(map(lambda x: x * 10, lst))
tpl = tuple(map(lambda x: x * 10, lst))
tpl

Output:

<map object at 0x00000250CB0D5F40>

(10, 20, 30, 40, 50)

map() 和 filter() 函式之間的一個重要區別是第一個函式總是返回與原始函式相同長度的迭代。因此由於 pandas Series 物件也是可迭代的,我們可以在 DataFrame 列上應用 map() 函式來建立一個新列:

import pandas as pd
df = pd.DataFrame({'col1': [1, 2, 3, 4, 5], 'col2': [0, 0, 0, 0, 0]})
print(df)
df['col3'] = df['col1'].map(lambda x: x * 10)
df

Output:

col1  col2
0     1     0
1     2     0
2     3     0
3     4     0
4     5     0

   col1  col2  col3
0     1     0    10
1     2     0    20
2     3     0    30
3     4     0    40
4     5     0    50

當然要在上述情況下獲得相同的結果,也可以使用 apply() 函式:

df['col3'] = df['col1'].apply(lambda x: x * 10)
df

Output:

col1  col2  col3
0     1     0    10
1     2     0    20
2     3     0    30
3     4     0    40
4     5     0    50

我們還可以根據某些條件為另一列建立一個新的 DataFrame 列,對於下面的程式碼,我們可以互換使用 map() 或 apply() 函式:

df['col4'] = df['col3'].map(lambda x: 30 if x < 30 else x)
df

Output:

col1  col2  col3  col4
0     1     0    10    30
1     2     0    20    30
2     3     0    30    30
3     4     0    40    40
4     5     0    50    50

帶有 reduce() 函式的 Lambda

reduce() 函式與 functools Python 模組相關,它的工作方式如下:

  • 對可迭代物件的前兩項進行操作並儲存結果
  • 對儲存的結果和可迭代的下一項進行操作
  • 以這種方式在值對上進行,直到所有專案使用可迭代的

該函式與前兩個函式具有相同的兩個引數:一個函式和一個可迭代物件。但是與前面的函式不同的是,這個函式不需要傳遞給任何其他函式,直接返回結果標量值:

from functools import reduce
lst = [1, 2, 3, 4, 5]
reduce(lambda x, y: x + y, lst)

Output:

15

上面的程式碼展示了我們使用 reduce() 函式計算列表總和時的作用

需要注意的是,reduce() 函式總是需要一個帶有兩個引數的 lambda 函式,而且我們必須首先從 functools Python 模組中匯入它

Python 中 Lambda 函式的優缺點

優點

  • 它是評估單個表示式的理想選擇,應該只評估一次
  • 它可以在定義後立即呼叫
  • 與相應的普通語法相比,它的語法更緊湊
  • 它可以作為引數傳遞給高階函式,例如 filter()、map() 和 reduce()

缺點

  • 它不能執行多個表示式
  • 它很容易變得麻煩,可讀性差,例如當它包括一個 if-elif-...-else 迴圈
  • 它不能包含任何變數賦值(例如,lambda x: x=0 將丟擲一個語法錯誤)
  • 我們不能為 lambda 函式提供文件字串

總結

總而言之,我們已經詳細討論了在 Python 中定義和使用 lambda 函式的許多方面:

  • lambda 函式與普通 Python 函式有何不同
  • Python 中 lambda 函式的語法和剖析
  • 何時使用 lambda 函式
  • lambda 函式的工作原理
  • 如何呼叫 lambda 函式
  • 呼叫函式執行(IIFE)的定義
  • 如何使用 lambda 函式執行條件操作,如何巢狀多個條件,以及為什麼我們應該避免它
  • 為什麼我們應該避免將 lambda 函式分配給變數
  • 如何將 lambda 函式與 filter() 函式一起使用
  • 如何將 lambda 函式與 map() 函式一起使用
  • 我們如何在 pandas DataFrame 中使用
  • 帶有傳遞給它的 lambda 函式的 map() 函式 - 以及在這種情況下使用的替代功能
  • 如何將 lambda 函式與 reduce() 函式一起使用
  • 在普通 Python 上使用 lambda 函式的優缺點

以上就是本次分享的所有內容,如果你覺得文章還不錯,歡迎關注公眾號: Python程式設計學習圈 ,每日干貨分享,內容覆蓋Python電子書、教程、資料庫程式設計、Django,爬蟲,雲端計算等等。或是前往程式設計學習網,瞭解更多程式設計技術知識。