1. 程式人生 > 其它 >Python高階函式--map、lambda、reduce、filter、zip

Python高階函式--map、lambda、reduce、filter、zip

技術標籤:python程式語言

一、map()函式

map()是 Python 內建的高階函式,它接收一個函式 f 和一個 list,並通過把list 的每個元素依次作用在函式 f 上,得到一個新的 list 並返回。 例如,對於list [1, 2, 3, 4, 5, 6, 7, 8, 9] 如果希望把list的每個元素都作平方,就可以用map()函式,我們只需要傳入函式f(x)=x*x,就可以利用map()函式完成這個計算:

def f(x):
    return x*x
print map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])

輸出結果: [1, 4
, 9, 10, 25, 36, 49, 64, 81]

注意:map()函式不改變原有的 list,而是返回一個新的 list。 利用map()函式,可以把一個 list 轉換為另一個 list,只需要傳入轉換函式。 由於list包含的元素可以是任何型別,因此,map() 不僅僅可以處理只包含數值的 list,事實上它可以處理包含任意型別的 list,只要傳入的函式f可以處理這種資料型別。

任務 假設使用者輸入的英文名字不規範,沒有按照首字母大寫,後續字母小寫的規則,請利用map()函式,把一個list(包含若干不規範的英文名字)變成一個包含規範英文名字的list: 輸入:['adam', 'LISA', 'barT']

輸出:['Adam', 'Lisa', 'Bart']

'''
遇到問題沒人解答?小編建立了一個Python學習交流QQ群:778463939
尋找有志同道合的小夥伴,互幫互助,群裡還有不錯的視訊學習教程和PDF電子書!
'''
def format_name(s):
    s1=s[0:1].upper()+s[1:].lower();
    return s1;

print map(format_name, ['adam', 'LISA', 'barT'])

map()函式是python內建的高階函式,對傳入的list的每一個元素進行對映,返回一個新的對映之後的list

python3中,map函式返回的是一個map物件,需要list(map(fun,itor))來將對映之後的map物件轉換成列表

二、lambda 函式

python 使用 lambda 來建立匿名函式。

  • lambda只是一個表示式,函式體比def簡單很多。
  • lambda的主體是一個表示式,而不是一個程式碼塊。僅僅能在lambda表示式中封裝有限的邏輯進去。
  • lambda函式擁有自己的名稱空間,且不能訪問自有引數列表之外或全域性名稱空間裡的引數。
  • 雖然lambda函式看起來只能寫一行,卻不等同於C或C++的行內函數,後者的目的是呼叫小函式時不佔用棧記憶體從而增加執行效率。
    語法

lambda函式的語法只包含一個語句,如下:

lambda [arg1 [,arg2,.....argn]]:expression 

以lambda x: x+1為例,首先,它是一個函式:

def f(x):
return x+1

好,這個函式引用時需要傳入一個引數,並且有一個返回值。這個引數一般是for x in L之類的傳進來,或者直接呼叫f(3)。

(1)先看第一個例子

f = lambda x: x**2
print(f(5)) # 25

結果是25,很easy,這裡要說明的是lambda x: x**2是一個函式,你如果print(f)的得到的是一個函式的地址,記住它是一個函式。

(2)和append搭配、在for迴圈內部

'''
遇到問題沒人解答?小編建立了一個Python學習交流QQ群:778463939
尋找有志同道合的小夥伴,互幫互助,群裡還有不錯的視訊學習教程和PDF電子書!
'''
for x in range(5):
  li.append(lambda x: x**2)
print(li[0](2)) # 4
print(li[1](3)) # 9

注:此處省略li = []的初始化程式碼,後續一樣
li是一個list,但是list裡面存的可不是數,而是函式地址,而且是5個x**2的函式,所以無論你是li[0](2)還是li[1](2),結果都是4。一般情況下不會這樣寫程式,因為沒什麼用途。

這裡說一下,看過一個程式這樣寫,猜測原作者是想讓li在運算時append的是資料,或者是以為這樣可以讓li在呼叫時n的值不隨x變,不管這樣,這個程式實際效果和上面一樣,x本身在變,n = x寫不寫沒有區別,li內部仍然是5個一樣的函式的地址。

for x in range(5):
  li.append(lambda n=x: n**2)
print(li[0](2)) # 4
print(li[1](3)) # 9

總結一下:lambda在for迴圈內部,和append搭配時,for迴圈不是為了給函式傳遞引數,只是為了生成多個函式。

(3)只和append搭配

'''
遇到問題沒人解答?小編建立了一個Python學習交流QQ群:778463939
尋找有志同道合的小夥伴,互幫互助,群裡還有不錯的視訊學習教程和PDF電子書!
'''
li.append(lambda x: x**2)
print(li[0](1)) # 1
print(li[0](3)) # 9
print(li[1](3)) # IndexError: list index out of range

這兒說的是另外一種情況,程式中並沒有給出匿名函式lambda的引數,在呼叫時才會給。而且li僅僅append了一次,所以li內部也僅有一個函式地址。呼叫時就不會有li[1]這種情況。

補充一種它的變形,說明一下對於這種情況,引數賦初值並無意義。

li.append(lambda x=5: x**2)
print(li[0](1)) # 1
print(li[0](3)) # 9
print(li[1](3)) # IndexError: list index out of range

(4)和append搭配、引數由for迴圈給出

舉個栗子

li.append(lambda :x for x in range(10))
print(next(li[0])()) # 0
print(next(li[0])()) # 1
print(next(li[1])()) # IndexError: list index out of range

此處有大坑,首先你得認出來(lambda :x for x in range(10))這種形式可沒有那麼簡單,這是產生一個生成器最簡單的方法,它的返回值是一個generator,所以li內部就存了一個generator。還有此時的函式是沒有引數的,等效為:

def f():
return x

有人會說這個函式有什麼意義嗎,是沒什麼意義,但是如果return x**2,其實還是有些意義的。

(5)放在[]中、引數由for迴圈給出

li = [lambda :x for x in range(10)]
print(li[0]()) # 9
print(li[1]()) # 9

這個函式其實不好理解,首先別看成生成器了,跟它沒關係。
lambda :x仍然是一個函式(return x),在沒有print(li[0]())之前它是不會被執行的,一旦執行print(li[0]()),就會輸出x的值,那麼x是多少呢,顯然x在上一句程式裡面已經變成9了,所以結果都是9,這裡其實是閉包的問題,想避免這個問題,程式就不能寫這麼簡潔了。

'''
遇到問題沒人解答?小編建立了一個Python學習交流QQ群:778463939
尋找有志同道合的小夥伴,互幫互助,群裡還有不錯的視訊學習教程和PDF電子書!
'''
for x in range(5):
def f():
return x**2
li.append(f())# instant run
print(li[0], li[1], li[2], li[3], li[4])

結果是0, 1, 4, 9, 16,是我們想要的,有人會說這兒為什麼不把def f()簡化一下呢?還真不能簡化,比較結果便知:

for x in range(5):
li.append(lambda :x**2) # uninstant run
print(li[0](), li[1](), li[2](), li[3](), li[4]())
#16 16 16 16 16

看到區別了吧,f 是一個函式地址,而 f() 是一個函式被執行後的返回值,所以第一個程式可以得到每次迴圈的 x 值。

(6)lambda最常用:和map、reduce、filter等結合用

其實lambda最常用的還是和map、reduce、filter這些高階函式結合使用,不過那個時候就把它當做一個函式,而且格式相對固定,具體使用就看高階函式的使用規則,較為簡單,就不展開。

#我們原來的函式是這樣的
def square_z(x):
    return xx
#現在我們可以寫成下面這樣(冒號後面的一切都是對輸入的操作,然後lambda x會返回結果):square1=lambda x:xx
print(square1(2))
#配合map,filter等lambda能發揮更大作用,一行程式碼就能列印列表內元素的平方數
print(list(map(lambda x:x*x,[1,2,3,4,5])))
print(list(filter(lambda x:x<3,[1,2,3,4,5])))

三、歸納函式(reduce):

第一個引數是函式名,第二個引數是sequence(序列,像list,tuple,str,set,dict都可以)

效果是這樣的:reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

'''
遇到問題沒人解答?小編建立了一個Python學習交流QQ群:778463939
尋找有志同道合的小夥伴,互幫互助,群裡還有不錯的視訊學習教程和PDF電子書!
'''
#提前準備一個函式,計算兩數之和
from functools import reduce
def add_z(x,y):
return x+y
#計算1~10之間數字之和(還是不包含10)
r_z=reduce(add_z,range(1,10))
print(r_z)
print("r_z 的型別:%s"%type(r_z))

結果如下:
在這裡插入圖片描述
四、過濾器(filter):

第一個引數是函式名,用於篩選的函式,第二個引數是Iterable(list,tuple,set,dict,str),返回一個filter且filter屬於Iterator

#用於過濾掉一切不需要的東西,下面我們以列印1~10之間的奇數為例說明:
from collections import Iterable,Iterator
#提前準備一個函式,判斷是否為奇數
def odd_z(x):
    if x%2==1:
        return True
    else:
        return False
f=filter(odd_z,range(1,10))
print("f 的型別:%s"%type(f))
print("f 是Iterator:%s"%isinstance(f,Iterator))
try:
    print(next(f),end='*')#f作為Iterator使用
    print(next(f),end='*')
except:
    print("\n結束了")
for i in f:#f作為Iterable使用
    print(i,end='$')

結果如下:
在這裡插入圖片描述
五、zip函式

zip() 函式用於將可迭代物件作為引數,將物件中對應的元素打包成一個個元組,然後返回由這些元組組成的物件。

如果各個可迭代物件的元素個數不一致,則返回的物件長度與最短的可迭代物件相同。

利用 * 號操作符,與zip相反,進行解壓。

語法:

zip(iterable1,iterable2, ...)

引數說明:

iterable–一個或多個可迭代物件(字串,列表,元組,字典)

舉例說明

'''
遇到問題沒人解答?小編建立了一個Python學習交流QQ群:778463939
尋找有志同道合的小夥伴,互幫互助,群裡還有不錯的視訊學習教程和PDF電子書!
'''
a = [1, 2, 3]
b = [4, 5, 6]
c = [7, 8, 9]
z = zip(a, b, c)

print('z值:', list(z))

print('打包a、c列表:', list(zip(a, c)))

z = zip(a, b)
print('解壓z:', list(zip(*z)))

# 字典形式
v1 = {1: 11, 2: 22}
v2 = {3: 33, 4: 44}
v3 = {5: 55, 6: 66}
v = zip(v1, v2, v3)
print('v值:', list(v))
w = zip(*zip(v1, v2, v3))
print("w值:", list(w))

# 搭配for迴圈
list1 = [2, 3, 4]
list2 = [5, 6, 7]
for x, y in zip(list1, list2):
    print(x, y, '--', x * y)

結果:

/usr/local/bin/python3.9 /Users/chenshifeng/MyCode/PythonCode/SFDSZL/1/a.py
z值: [(1, 4, 7), (2, 5, 8), (3, 6, 9)]
打包a、c列表: [(1, 7), (2, 8), (3, 9)]
解壓z: [(1, 2, 3), (4, 5, 6)]
v值: [(1, 3, 5), (2, 4, 6)]
w值: [(1, 2), (3, 4), (5, 6)]
2 5 -- 10
3 6 -- 18
4 7 -- 28

Process finished with exit code 0