Python 進階之路 (五) map, filter, reduce, zip 一網打盡

alpha94511發表於2019-02-11

簡潔的內建函式

大家好,我又回來了,今天我想和大家分享的是Python非常重要的幾個內建函式:map,filter,reduce, zip。
它們都是處理序列的便捷函式。這很大程度上歸功於函數語言程式設計的世界。我們可以利用它們把一些小函式應用於一個序列的所有元素。從而節省編寫顯式迴圈的時間。

另外,這些中的每一個都是純函式,有返回值。因此我們可以容易地將函式的返回結果用表示式來表示。

好了,又到了大白話時間,為什麼用它們,就是可以簡化我們的程式碼,更簡潔高效的執行一些需要用到迴圈迭代為主的任務,接下來讓我們一個個來看

map()

函式構造

map()函式的主要作用是可以把一個方法依次執行在一個可迭代的序列上,比如List等,具體的資訊如下:

  • 基礎語法:map(fun, iterable)
  • 引數:fun是map傳遞給定可迭代序列的每個元素的函式。iterable是一個可以迭代的序列,序列中的每一個元素都可以執行fun
  • 返回值:map object

好了,大白話就是利用map我們可以把一個函式fun 執行到序列iter的每一個元素上,用例子非常好理解~

基礎用法:

下面先讓我們看一個小例子,假設現在我們有一個List,包含1~5五個數字,我們想要讓每一個數+1,如果不知道map這個函式之前,我們的解決方案是這樣的:

numbers = [1, 2, 3, 4, 5]
for i in range(0,len(numbers)):      #對每個元素加1
    numbers[i]+=1 
print(numbers)
Out:[2, 3, 4, 5, 6]

或者是這樣的:

numbers = [1, 2, 3, 4, 5]
result = []
for n in numbers:
    result.append(n+1)
print(result)
Out:[2, 3, 4, 5, 6]

但是顯然,無論怎麼做都會涉及到寫迴圈,這裡就是map函式的用武之地了,我們可以用map函式這樣實現:

def add_one(n):
    return n + 1

numbers = [1, 2, 3, 4, 5]
result = map(add_one, numbers)
print(result)
print(type(result))
print(list(result))

Out:<map object at 0x00000260F508BE80>
    <class `map`>
    [2, 3, 4, 5, 6]

這裡想必聰明的你發現了map的好處,在優化精簡程式碼的同時,某種程度上講實現了方法和迴圈部分的分離,這裡我們可以發現map返回就是map類,我們這裡傳遞的序列是List,最後輸出時經過型別轉換也是list

在傳遞序列時只要這個序列是可迭代的就好,不一定非要List,比如我們換一種:

def add_one(n):
    return n + 1
    
numbers = (1, 2, 3, 4, 5)     #序列為元組
result = map(add_one, numbers)
print(tuple(result))          #

Out:(2, 3, 4, 5, 6)

輸入的序列為同樣可以迭代的元組,輸出時我們也選擇元組,效果一樣的。

更進一步

還用剛才的例子,為了更加簡潔,我們可以用lambda函式配合map使用,具體實現如下:

numbers = (1, 2, 3, 4, 5)                     # 迭代物件為tuple
result = map(lambda x: x + 1, numbers)
print(list(result))                           # 輸出物件為list
 
Out:[2, 3, 4, 5, 6]

更加簡潔優雅了對吧!!這個lambad函式我之後會說,今天它不是主角哈哈,先一帶而過。
讓我們重新把目光轉移到map上來,除了剛才的用法,還要一種情況也十分常見,讓我們看下面的例子:

# List of strings
words = [`paris`, `xiaobai`,`love`]

# 把陣列中每個元素變為List
test = list(map(list, words))
print(test)

Out: [[`p`, `a`, `r`, `i`, `s`], [`x`, `i`, `a`, `o`, `b`, `a`, `i`], [`l`, `o`, `v`, `e`]]

words是一個只包含字串型別元素的list,我們用map可以實現將words的每一個元素全部轉化為list型別,這裡有一點一定要注意,能實現的前提一定是每個元素都是可以迭代的型別,如果出現瞭如int型別的元素,就會出錯啦:

# List of strings
words = [18,`paris`, `xiaobai`,`love`]

# 把陣列中每個元素變為List
test = list(map(list, words))
print(test)

Out:TypeError: `int` object is not iterable

大家一看錯誤型別相比立刻就明白啦,所以正確的使用方法一定是類似這種:

nums = [3,"23",-2]
print(list(map(float,nums)))

Out: [3.0, 23.0, -2.0]

總之就是型別要注意,今天我就拋磚引玉簡單介紹一下map,具體的用法大家可以自行開發哈,我也在不斷學習中

filter()

函式構造

filter()方法藉助於一個函式來過濾給定的序列,該函式測試序列中的每個元素是否為真。

  • 基礎語法:filter(fun, iterable)
  • 引數:fun測試iterable序列中的每個元素執行結果是否為True,iterable為被過濾的可迭代序列
  • 返回值:可迭代的序列,包含元素對於fun的執行結果都為True

簡而言之就是filter可以幫助我們根據給出的條件過濾一組資料並返回結果

基礎用法:

讓我們先看一個例子:

# 過濾母音的方法
def fun(variable):
    letters = [`a`, `e`, `i`, `o`, `u`]
    if (variable in letters):
        return True
    else:
        return False

# 給定序列
sequence = [`I`, `l`, `o`, `v`, `e`, `p`, `y`,`t`,`h`,`o`,`n`]

# 根據條件得出結果
filtered = list(filter(fun, sequence))
print(filtered)

Out:[`o`, `e`, `o`]

這裡我們建立一個可以提取母音字母的方法fun,給定的可迭代序列為list,之後就可以用filter方法很容易的提取出結果啦,再看一個類似例子:

# 判斷為正數
def positive(num):
    if num>0:
        return True
    else:
        return False

#判斷偶數
def even(num):
    if num % 2==0:
        return True
    else:
        return False

numbers=[1,-3,5,-20,0,9,12]

positive_nums = list(filter(positive, numbers))
print(positive_nums)  # 輸出正數 list


even_nums = tuple(filter(even,numbers))
print(even_nums)     #輸出偶數 tuple

Out:[1, 5, 9, 12]
     (-20, 0, 12)

看到這裡相比大家已經知道filter的基礎用法啦, 要先有一個,能返回True或者False的方法,或者表示式作為過濾條件就行啦

更進一步

這裡其實和map一樣了,基本上最簡潔的用法都是和lambda混在一起,比如下面我們想要把剛才的一大串程式碼壓縮一下:

numbers = [0, 1, 2, -3, 5, -8, 13]

# 提取奇數
result = filter(lambda x: x % 2, numbers)
print("Odd Numbers are :",list(result))

# 提取偶數
result = filter(lambda x: x % 2 == 0, numbers)
print("Even Numbers are :",list(result))

#提取正數
result = filter(lambda x: x>0, numbers)
print("Positive Numbers are :",list(result))

Out:Odd Numbers are : [1, -3, 5, 13]
     Even Numbers are : [0, 2, -8]
     Positive Numbers are : [1, 2, 5, 13]

” 爽啊!爽死了!” 郭德綱看到後這麼評價,lambda我平時用的不多,但是寫到這裡,我也覺得要好好學習它了,畢竟和其他程式語言相比,可能這中用法才是python提倡的理念之一:高效簡潔,

reduce()

函式構造

Reduce是一個非常有用的函式,用於在列表上執行某些計算並返回結果。它將滾動計算應用於列表中的連續值。例如,如果要計算整數列表的累積乘,或者求和等等

  • 基礎語法:reduce(function, iterable)
  • 引數:fun是連續作用於iterable每一個元素的方法,新的引數為上一次執行的結果,iterable為被過濾的可迭代序列
  • 返回值:最終的function返回結果

在Python 2中,reduce()是一個內建函式。但是,在Python 3中,它被移動到functools模組。因此,要使用前我們需要匯入,這裡我的環境是Python 3.6

基礎用法:

先看一個求累加和的小栗子:

from functools import reduce # Python 3

def do_sum(x1, x2): 
    return x1 + x2
    
print(reduce(do_sum, [1, 2, 3, 4]))

Out:10

再看一個累積乘法的例子:

from functools import reduce # Python 3
def multiply(x, y):
    return x*y

numbers = [1,2,3,4]
print(reduce(multiply, numbers))

Out:24

更進一步:

還是和lambda混搭,更加簡潔:

from functools import reduce # Python 3
numbers = [1,2,3,4]
result_multiply = reduce((lambda x, y: x * y), numbers)
result_add = reduce((lambda x,y: x+y), numbers)

print(result_multiply)
print(result_add)

Out:24
     10

zip()

函式構造

zip()的目的是對映多個容器的相似索引,以便它們可以僅作為單個實體使用。

  • 基礎語法:zip(*iterators)
  • 引數:iterators為可迭代的物件,例如list,string
  • 返回值:返回單個迭代器物件,具有來自所有容器的對映值

基礎用法:

其實之前我們在講dict的建立方法時提到過它,這裡從新回顧一下:


keys = [`name`,`age`]
values = [`xiaobai`,18]
my_dict = dict(zip(keys,values))
print(my_dict)

Out:{`name`: `xiaobai`, `age`: 18}

zip可以支援多個物件,比如下面的例子

name = [ "xiaobai", "john", "mike", "alpha" ]
age = [ 4, 1, 3, 2 ]
marks = [ 40, 50, 60, 70 ]

# using zip() to map values
mapped = list(zip(name, age, marks))
print ("The zipped result is : "mapped)

Out:The zipped result is : [(`xiaobai`, 4, 40), (`john`, 1, 50), (`mike`, 3, 60), (`alpha`, 2, 70)]

這裡我們可以很容易的的把name,age,marks這三個list裡面相同index的值對映打包在一起

更進一步:

通過上面的例子,我們發現可以很容易的以類似1對1的形式把不同物件的同一索引位置的值打包起來,那如果是解包呢?也是類似的,就是多了一個 * 而已

names, ages, marks = zip(*mapped)
print ("The name list is : ",names)
print ("The age list is : ",ages)
print ("The marks list is : ",marks)

Out: The name list is :  (`xiaobai`, `john`, `mike`, `alpha`)
     The age list is :  (4, 1, 3, 2)
     The marks list is :  (40, 50, 60, 70)

總結

今天主要為大家介紹了map,filter,reduce,zip四個高效的python內建函式的用法,我也是剛剛接觸,瞭解不夠深入,如果介紹的有錯誤或者歧義還請大家多多諒解和包容,如果有大神可以進一步補充說明一定要寫個評論呀,讓我們一起進步。

最後為大家講個悲傷的故事:

clipboard.png

相關文章