2021年11月6日 星期六

Python 學習筆記 : 內建函式 map() 的用法

Python 的 map() 是把傳入之函式套用 (apply) 到可迭代物件的每一個元素上, 然後將運算結果打包成迭代器物件傳回來, 關於迭代參考 :

本篇測試參考了下列書籍 :

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 的元素在迭代中耗盡後映射就停止了.

沒有留言:

張貼留言