Python 中的 Map 函數處理可迭代對象
?本文中,我們將一起學習Python的 map() 如何工作;如何使用 map() 轉換不同類型的 Python 迭代變量。

有了這些知識,我們就能在程序中有效地使用map(),或者使用列表推導式或生成器表達式來使代碼更加Pythonic。
為了更好地理解map(),需要一些前置知識,如了解如何使用迭代器、for循環、函數和lambda函數的一些知識。
可以查看我們日前詳細介紹過的戳??:Python 中的萬能之王 Lambda 函數
Python中的函數式編程
在函數式編程中,計算是通過組合函數來完成的,這些函數接受參數并返回一個(或多個)具體數值作為結果。這些函數不修改其輸入參數,也不改變程序的狀態。它們只是提供一個特定的計算結果。這些類型的函數通常被稱為純函數。
從理論上講,使用函數式編程風格構建的程序將更易運行。
- 開發,因為我們可以單獨編碼和使用每一個函數
- 調試和測試,因為我們可以測試和調試個別功能,而不看程序的其他部分。
- 理解,因為我們不需要處理整個程序的狀態變化
函數式編程通常使用列表、數組和其他可迭代的數據類型,以及一組對數據進行操作和轉換的函數來表示。當涉及到用函數式處理數據時,至少有三種常用的技術。
Mapping 包括對可迭代對象應用轉換函數來生成新的可迭代對象。通過對原始可迭代對象中的每一項調用轉換函數來生成新的可迭代對象中的項。
Filtering 包括對可迭代對象應用謂詞或布爾值函數來生成新的可迭代對象。新可迭代對象中的項是通過過濾掉原始可迭代對象中使謂詞函數返回False的任何項來生成的。
Reducing 括對可迭代對象應用reduce函數來產生單個累積值。
早在1993年,Python社區就要求有一些函數式編程的特性,他們要求的是
- 一個Lambda 匿名函數
- 一個map()函數
- 一個filter()函數
- 一個reduce()函數
由于一位社區成員的貢獻,這些函數式特性被添加到語言中。現在,map()、filter()和reduce()是Python中函數式編程風格的基本組成部分。
在本文中,我們將了解這些函數之一,即內置函數map()?。我們還將學習如何使用列表推導式和生成器表達式,以Pythonic和可讀的方式獲得map()的相同功能。
開始使用 map() 函數
有時我們會面臨這樣的情況:需要對輸入可迭代對象的所有項執行相同的操作來構建一個新的可迭代對象。解決這個問題的最快速、最常見的方法是使用一個 Python for 循環。然而也可以通過使用 map() 來解決這個問題,而不需要顯式循環。
在下面,我們將一起學習 map() 的工作原理,以及如何使用它來處理和轉換迭代變量而不需要循環。
入門 map()
map()循環遍歷輸入可迭代對象(或可迭代對象)的項,并返回一個迭代器,該迭代器是對原始輸入可迭代對象中的每個項應用轉換函數得到的結果。
根據 文檔[1],map()接受一個函數對象和一個可迭代對象(或多個可迭代對象)作為參數,并返回一個迭代器,根據需要生成轉換后的條目。函數簽名定義如下:
map()?將函數應用于循環中可迭代對象中的每個元素,并返回一個新的迭代器,按需生成轉換后的元素。函數可以是任何Python函數,其參數的數量等于傳遞給 map() 的可迭代對象的數量。
注意: map() 的第一個參數是一個函數對象,需要傳遞一個函數而不調用它。即不需要使用一對圓括號。
map()的這個第一個參數是一個轉換函數。換句話說,它是將每個原始項轉化為新的 (已轉化) 項的函數。盡管 Python 文檔中稱這個參數為函數,但它可以是任何 Python 可調用的函數,這包括內置函數、類、方法、lambda 函數和用戶定義的函數。
map()?執行的操作通常被稱為mapping,因為它將輸入可迭代對象中的每一項映射到結果可迭代對象中的新項。為此,map()對輸入的可迭代對象中的所有項應用一個轉換函數。
為了更好地理解map(),假設需要取一個數值列表,并將其轉化為一個包含原列表中每個數字的平方值的列表。此時可以使用一個for循環,代碼如下。
當在 numbers?上運行這個循環時,會得到一個平方值列表。for? 循環遍歷 numbers?,并對每個值應用冪運算。最后,它將結果值存儲在squared中。
使用map()可以在不使用顯式循環的情況下獲得相同的結果。看看上面示例的以下重新實現:
square()?是一個轉換函數,將一個數字映射到它的平方值。調用map()將square()?應用于numbers?中的所有值,并返回一個生成平方值的迭代器。然后在map()?上調用 list() 來創建一個包含平方值的列表對象。
由于map()?是用C語言編寫的,并且是高度優化的,它的內部隱含循環可以比普通的Python for循環更有效率。這是使用map()的一個優點。
map()?的第二個優點與內存消耗有關。使用for循環時,需要將整個列表存儲在系統的內存中。使用map(),可以按需獲得項目,并且在給定的時間內只有一個項目在系統的內存中。
注意: 在Python中2。X,map()? 返回一個列表。此行為在Python 3.x中發生了改變。現在,map()?返回一個map對象,這是一個迭代器,可生成按需的項目。這就是為什么我們需要調用 list() 來創建想要的列表對象。
再比如,我們需要將一個列表中的所有項目從字符串轉換為整數。要做到這一點,我們可以使用map()和int(),如下所示。
map()? 將 int()? 應用于 str_nums 中的每個值。由于 map()? 返回一個迭代器(一個map對象),需要調用 list(),這樣我們就可以用完迭代器并將其變成一個list對象。注意,在這個過程中,原始序列不會被修改。
在不同類型的函數中使用map()
我們可以用 map()? 來使用任何種類的Python可調用函數。唯一的條件是該可調用函數需要一個參數,并返回一個具體而有用的值。例如,我們可以使用類、實現了特殊方法 __call__()的實例、實例方法、類方法、靜態方法以及函數。
有一些內置的函數,我們可以和 map() 一起使用。請看下面的例子。
我們可以用 map() 使用任何內置函數,只要該函數接受一個參數并返回一個值。
在使用map()?時,我們會看到一個常見的模式是使用一個lambda函數作為第一個參數。當我們需要向map()傳遞一個基于表達式的函數時,lambda函數就很方便。例如,我們可以用lambda函數重新實現平方值的例子,如下所示。
在使用 map()? 時,lambda函數是相當有用的。它們可以發揮 map() ?的第一個參數的作用。我們可以在使用 map() 的同時使用lambda函數來快速處理和轉換我們的迭代變量。
用map()處理多個輸入可迭代對象
如果為map()?提供了多個迭代變量,那么轉換函數必須接受與傳遞的迭代變量同樣多的參數。map()的每一次迭代將從每個迭代器中傳遞一個值作為參數給函數,迭代在最短的迭代器的末端停止。
以下使用 pow() 的例子。
pow()? 接收兩個參數,x和y?,并返回x到y?的冪。在第一次迭代中,x是1,y是4?,結果是1?。在第二次迭代中,x是2,y是5?,結果是32?,以此類推。最后的可迭代的長度只相當于最短的可迭代的長度,在這種情況下就是first_it。
我們可以使用不同種類的數學運算來合并兩個或多個數值迭代表。下面是一些使用lambda函數對幾個輸入迭代表進行不同數學運算的例子。
在第一個示例中,使用減法操作合并兩個可迭代對象,每個可迭代對象包含三個元素。在第二個示例中,將三個可迭代對象的值相加。
用map()轉換字符串的可迭代對象
當處理字符串對象的可迭代對象時,我們可以借助Python的map()?,使用某種轉換函數轉換所有對象。下面我們通過一些示例,了解如何使用 map()轉換字符串對象的可迭代對象。
使用 str?的方法
處理字符串的一個很常見的方法是使用str?類的一些方法將給定的字符串轉換為一個新的字符串。如果我們正在處理字符串的迭代表,并且需要對每個字符串應用相同的轉換,那么我們可以使用 map() 和各種字符串方法。
使用map() 和str?方法,可以對string_it ?中的每一項執行一些轉換。大多數時候,使用不帶附加參數的方法,比如str.capitalize()?, str.lower()?, str.swapcase()?, str.title()?,以及str.upper()。
我們也可以使用一些帶有默認值的附加參數的方法,比如str.strip()?,它需要一個叫做char的可選參數,默認為刪除空白。
當像這樣使用 str.strip()? 時,其實是傳入默認值char?。在這種情況下,我們使用 map()? 來刪除 with_spaces 項中的所有空白。
注意: 如果需要提供參數而不是使用默認值,可以使用lambda函數。
下面是一個使用 str.strip() 去除點而不是默認的空白的例子。
lambda?函數在字符串對象s?上調用.strip(),刪除所有前導和尾部的點。
例如,當處理文本文件時,其中的行可能有尾部的空格(或其他字符),而需要刪除它們時,這種技術就會很方便。如果是這種情況,那么需要考慮使用str.strip()而不使用自定義字符,也會刪除換行字符。
刪除標點符號
有時在處理文本時,需要刪除將文本分割成單詞后留下的標點符號。我們可以創建一個自定義函數,使用匹配最常見的標點符號的正則表達式來刪除單個單詞的標點符號。
下面是使用sub()[2]實現該函數的可能方法,它是一個存在于Python標準庫中的re[3]模塊中的正則表達式函數:
在自定義函數remove_punctuation()?中,使用一個正則表達式模式來匹配在任何英文文本中發現的最常見的標點符號。對re.sub()?的調用使用一個空字符串("")替換了匹配的標點符號,并返回一個干凈的單詞。
有了轉換函數,我們可以使用map()對文本中的每個字進行轉換。下面是它的工作原理。
在這段文字中,有些詞包括標點符號。例如,我們有 'people,'?而不是 'people','problem,'?不是 'problem'?,等等。對map()?的調用將remove_punctuation()應用于每個詞,并刪除任何標點符號。因此,第二個列表中存儲的是處理后的干凈的單詞。
注意我們的正則表達式中沒有撇號('?),因為我們想將像I'll這樣的縮略語保持原貌。
實現凱撒密碼算法
羅馬政治家Julius Caesar?曾經用密碼來保護他發給他的將軍們的信息,用密碼來加密。Caesar cipher 凱撒密碼[4]將每個字母移位若干個字母。例如,如果我們將字母a?移位三個,那么我們就會得到字母d,以此類推。
如果移位超過了字母表的末端,那么我們只需要旋轉回字母表的開頭。在旋轉3的情況下,x?會變成a。以下是旋轉后字母表的樣子。
- 原文字母: abcdefghijklmnopqrstuvwxyz
- 字母表旋轉3: defghijklmnopqrstuvwxyzabc
下面的代碼實現了rotate_chr()?,這個函數接收一個字符并將其旋轉3圈。rotate_chr()將返回旋轉后的字符。下面是代碼。
在rotate_chr()中,首先檢查該字符是否在字母表中。如果不是,那么就返回相同的字符。這樣做的目的是為了保留標點符號和其他不尋常的字符。在第8行,計算該字符在字母表中的新的旋轉位置,這里要使用內置函數ord()[5]。
ord()?接收一個Unicode字符,并返回一個整數,代表輸入字符的 Unicode碼位。例如,ord("a")?返回97?,而ord("b")?返回98。
ord() 接收一個字符作為參數,并返回輸入字符的Unicode碼位。
如果把這個整數加到rot_by?的目標數上,那么將得到新字母在字母表中的旋轉位置。在這個例子中,rot_by?是3。因此,字母 "a"? 旋轉3下將成為位置100的字母,也就是字母 "d"?。字母 "b"? 旋轉三下將成為位置101?的字母,也就是字母 "e",以此類推。
如果字母的新位置不超過最后一個字母的位置(alphabet[-1]?),那么就返回這個新位置的字母,此時可以使用內置函數chr()。
chr()是ord()?的逆運算。它接收一個代表Unicode字符的Unicode碼位的整數,并返回該位置的字符。例如,chr(97)?將返回'a'?,而chr(98)?將返回'b'。
chr()接收一個整數,代表一個字符的Unicode碼位,并返回相應的字符。
最后,如果新的旋轉位置超出了最后一個字母的位置(alphabet[-1]?),那么需要旋轉回到字母表的開頭,此時需要從旋轉的位置減去字母表的長度(rotated_pos - len(alphabet)?),然后用chr()返回這個新位置的字母。
用rotate_chr()?作為轉換函數,可以用map()?用凱撒密碼算法對任何文本進行加密。下面是一個使用str.join()來連接字符串的例子。
字符串在Python中也是可迭代的。因此,對map()?的調用將rotate_chr()?應用于原始輸入字符串中的每個字符。在這種情況下,"M"? 變成 "p","y"? 變成 "b"?,等等。最后,對str.join()的調用將每個旋轉的字符連接到一個最終的加密信息中。
用map()轉換數字的可迭代對象
map()在處理和轉換數字值的迭代表方面也有很大潛力。可以進行各種各樣的數學和算術運算,將字符串值轉換成浮點數或整數,等等。
在下面的內容中,我們將一起學習一些如何使用map()來處理和轉換數字迭代表的例子。
使用數學運算
使用數學運算來轉換數值迭代的一個常見例子是使用冪運算符(**)。在下面的例子中,我們編寫了一個轉換函數,它接收一個數字并返回數字的平方和立方。
powers()?接受一個數字'x'?并返回它的平方和立方。由于Python將多個返回值作為元組處理,所以每次調用powers()?都會返回一個有兩個值的元組。當你用 powers()? 作為參數調用 map()時,你會得到一個包含輸入可迭代對象中每個數字的平方和立方的元組列表。
使用 map()? 可以執行許多與數學相關的轉換。您可以在每個值上添加常量或從它們中減去常量。您還可以使用 math 模塊中的一些函數,如sqrt() , factorial()? , sin(),cos() ?,等等。下面是一個使用 factorial() 的例子:
在這種情況下,我們將數字轉化為一個新的列表,包含原列表中每個數字的階乘。
我們可以使用'map()對數字可迭代對象執行廣泛的數學轉換。你深入到這個話題的程度取決于你的需求和你的想象力。考慮一下這個問題,編寫我們自己的示例代碼!
轉換溫度?
·的另一個用例是在測量單位之間進行轉換。假設我們有一個以攝氏度或華氏度測量的溫度列表,我們需要將它們轉換為相應的華氏度或攝氏度的溫度。
可以編碼兩個轉換函數來完成這個任務。
to_fahrenheit()? 接收攝氏溫度測量值,并將其轉換為華氏溫度。類似地,to_celsius()接收華氏溫度并將其轉換為攝氏溫度。
這些函數可以作為轉換函數使用,即可以將它們與map()一起使用,將溫度測量值的可迭代數據分別轉換為華氏和攝氏。
如果用to_fahrenheit()和celsius_temps?調用map()?,那么會得到一個華氏溫度測量值的列表。如果用to_celsius()和fahr_temps?來調用map(),那么會得到一個以攝氏度為單位的溫度測量列表。
要擴展這個例子并涵蓋任何其他種類的單位轉換,只需要編碼一個適當的轉換函數。
將字符串轉換為數字
在處理數字數據時,我們可能會遇到所有數據都是字符串值的情況。要做任何進一步的計算,我們需要將字符串值轉換成數字值。map()也可以幫助處理這些情況。
如果我們確定我們的數據是干凈的,不包含錯誤的值,那么我們可以根據我們的需要直接使用float()或int()。下面是一些例子。
在第一個例子中,使用float()與map()?將所有的值從字符串值轉換為浮點值。在第二個例子中,使用int()將字符串轉換為整數。注意,如果其中一個值不是一個有效的數字,那么會得到一個ValueError。
如果我們不確定數據是否干凈,那么可以使用一個更精細的轉換函數,如下面的。
在to_float()?中,使用try語句?,在轉換number? 時,如果 float()? 失敗,則捕獲 ValueError 。如果沒有發生錯誤,那么函數將返回轉換為有效浮點數的number? 。否則,將得到nan? (非數值) 值,這是一個特殊的 float 值,可以使用它來表示不是有效數字的值,就像上面示例中的 "One" 一樣。
我們可以根據需要定制 to_float()?。例如,我們可以用 return 0.0? 語句代替 return float("nan") 語句,等等。
在后續的文章中,我們將繼續學習如何將 map() 與其他函數工具 結合起來,并且為了使代碼更加Pythonic,使用列表推導式和生成器表達式來 替代 map() ,盡情期待~記得點贊和在看哦~
參考資料
[1]map()文檔: https://docs.python.org/3/library/functions.html#map
[2]sub(): https://docs.python.org/3/library/re.html#re.sub
[3]re: https://docs.python.org/3/library/re.html#module-re
[4]Caesar cipher 凱撒密碼: https://en.wikipedia.org/wiki/Caesar_cipher
[5]ord(): https://docs.python.org/3/library/functions.html#ord































