Python 的 map() 是把傳入之函式套用 (apply) 到可迭代物件的每一個元素上, 然後將運算結果打包成迭代器物件傳回來, 關於迭代參考 :
本篇測試參考了下列書籍 :
- 精通 Python (碁峰, 2020) 第 7 章
- Python 3 程式庫參考手冊 (碁峰, 2012) 第二章
- Python 函式庫語法範例字典 (旗標, 2019) 第 4-35 節
- Python 入門邁向高手之路王者歸來 (深石, 2017) 8-11 節
- The Python 3 Standard Library by Example (Edison Weslly, 2017)
- Python最強入門邁向數據科學之路:王者歸來(全彩印刷第二版) 8-10 節
map() 函式的功能是轉換 (transformation) 或映射 (mapping), 它可將多個可迭代物件的對應元素透過指定的函式進行運算, 結果會被放在 map 物件中傳回, 此 map 物件是一個產生器 (generator) 物件 (Python 2 的 map() 傳回的是串列, 但 Python 3 改為傳回產生器), 也是可迭代物件與迭代器物件, 故可用 next() 或 for 迴圈走訪, 也可用 list() 與 tuple() 轉成串列或元組.
關於產生器參考 :
map() 函式的語法如下 :
map(func, iterable1 [, iterable2, ....])
第一個必要參數是一個函式名稱或匿名函式 (lambda), 第二個必要參數為一個可迭代物件 (串列, 元組, 字串, 集合), 也可以傳入兩個以上的可迭代物件, 但要注意, 傳入第一參數 func 函式的引數個數必須與可迭代物件的個數相同, 這些引數依序代表各可迭代物件中的對應元素. 其次, 若傳入之多個可迭代物件長度不一, 則運算至長度最短的元素耗盡時映射即停止.
1. 傳入一個可迭代物件 :
只有傳入一個可迭代物件時, 映射的函式必須只傳入一個代表可迭代物件之引數 :
map(func, iterable)
在使用 map() 之前, 先來看看如果沒有 map() 函式的話要怎麼做映射, 要對一個可迭代物件例如串列的元素進行映射, 首先需要準備一個空串列, 接著在利用 for 迴圈走訪串列元素時進行映射 (運算), 然後將結果存入空的串列, 等迴圈結束即可得到映射結果, 例如 :
>>> list1=[1, 2, 3, 4, 5]
>>> list2=[] # 儲存映射結果的空串列
>>> for i in list1: # 走訪串列
list2.append(i ** 2) # 映射運算
>>> list2
[1, 4, 9, 16, 25]
此例透過迴圈將串列 list1 以平方運算映射到 list2, 如果改用 map() 來做寫法如下 :
>>> list1=[1, 2, 3, 4, 5]
>>> mapped=map(lambda x: x**2, list1) # 用 map() 與匿名函式映射 list1
>>> type(mapped) # map() 傳回 map 物件
<class 'map'>
>>> hasattr(mapped, '__iter__') # map 物件是可迭代物件
True
>>> hasattr(mapped, '__next__') # map 物件是迭代器物件
True
>>> list(mapped) # 轉成串列
[1, 4, 9, 16, 25]
>>> list(mapped) # 轉換後 map 物件內容被清空
[]
>>> mapped=map(lambda x: x**2, list1) # 重新建立 map 物件
>>> tuple(mapped) # 轉成元組
(1, 4, 9, 16, 25)
可見 map() 的傳回值是一個 map 物件, 也是可迭代與迭代器物件, 只能走訪或轉換一次.
用來映射的函式也可以式內建函式, 例如 :
>>> students=('Peter', 'Jim', 'Mike') # 元組
>>> list(map(len, students)) # 用內建函式 len() 計算字串長度
[5, 3, 4]
>>> list(map(int, ['-2', '3', '8', '-9'])) # 用內建函式 int() 將整數字串轉成數值
[-2, 3, 8, -9]
>>> list(map(str, [1, 2, 3, 4, 5])) # 將數值串列用 str() 映射為字串串列
['1', '2', '3', '4', '5']
2. 傳入兩個或更多可迭代物件 :
傳入 n 個可迭代物件時, 第一參數 func 必須傳入 n 個引數 (代表可迭代物件), 例如 :
>>> a=[1, 2, 3, 4, 5] # 第一個可迭代物件
>>> b=[11, 12, 13, 14, 15] # 第二個可迭代物件
>>> def func(a, b): # 映射函式
return a + b
>>> mapped=map(func, a, b) # 呼叫 map() 進行映射 (相加)
>>> type(mapped)
<class 'map'>
>>> mapped
<map object at 0x0000024D07D50550>
>>> list(mapped) # 相加結果轉成串列
[12, 14, 16, 18, 20]
下面範例是對三個串列進行映射 :
>>> a=[1, 2, 3, 4, 5] # 第一個可迭代物件
>>> b=[11, 12, 13, 14, 15] # 第二個可迭代物件
>>> c=[10, 11, 12, 13, 14] # 第三個可迭代物件
>>> mapped=map(lambda a, b, c: a + b -c, a, b, c) # 利用匿名函式映射
>>> list(mapped)
[2, 3, 4, 5, 6]
此例使用 lambda 匿名函式做 a + b - c 映射, 既然傳入三個串列, 則映射函式就必須接收三個引數, 就算故意忽略串列 c, 不讓它進行映射, 也必須帶進這三個引數. 此例若改為只要做 a + b 映射, 仍要在 lambda 中帶入三個引數, 否則會報錯, 例如 :
>>> mapped=map(lambda a, b: a + b, a, b, c) # 映射函式少了一個引數 c
>>> list(mapped) # 呼叫 map() 不會報錯, 轉換或走訪時才會
Traceback (most recent call last): #
File "<pyshell>", line 1, in <module>
TypeError: <lambda>() takes 2 positional arguments but 3 were given
>>> mapped=map(lambda a, b, c: a + b, a, b, c) # 引數都要全部帶入函式中
>>> list(mapped)
[12, 14, 16, 18, 20]
可見雖然我們只要用 a + b 做映射, 但既然傳入了三個可迭代物件, 則映射函式就一定要承接這三個物件 (雖然映射函式中用不到串列 c).
3. 傳入的可迭代物件長度不同 :
上面的例子中傳入 map() 的可迭代物件長度都相同, 如果不相同的話, 當長度最短的元素在迭代中耗盡時映射就會停止, 例如 :
>>> a=[1, 2, 3] # 第一個可迭代物件
>>> b=[11, 12, 13, 14, 15] # 第二個可迭代物件
>>> mapped=map(lambda x, y: x + y, a, b) # 呼叫 map() 用 a + b 映射
>>> list(mapped)
[12, 14, 16]
此例串列 a 長度為 3, 串列 b 長度為 5, 因此當 a 的元素在迭代中耗盡後映射就停止了.
沒有留言:
張貼留言