一文看懂Python列表表達式及高階函數如lambda, zip, enumerate, map和filter方法

深入瞭解和熟練使用python的列表表達式和內置的一些高階函數如lamda, zip, enumerate, map, filter, reduce, sorted方法是python學習,面試和工作的必備技能。正是它們給了python優秀的特性,從而使python有別於其它編程語言。網上的介紹和教程有很多,但感覺還是比較零散,解讀也不夠深入,小編決定再次動手寫篇總結。如果大家還沒讀過小編寫的一文看懂python系列文章,請先閱讀一文看懂Python面向對象編程(Python學習與新手入門必看)-絕對原創和一文看懂Python對文件和文件夾的操作: 含os, shutil和glob模塊。Python學習面試必讀。本文內含很多實例代碼,以幫助新手更好理解。

 

列表表達式[List Comprehension]

顧名思義,這個表達式作用是以一個快捷的方法對列表進行操作或運算,返回新的列表。其使用方式爲[表達式 for 變量 in 列表]  或者  [表達式 for 變量 in 列表 if 條件]。

 

一個最簡單的列表表達式的例子如下。
 

>>> list1 = [1, 2, 3, 4]
>>> list2 = [ i*i for i in list1]
>>> list3 = [ i*i for i in list1 if i>2]
>>> print(list2)
[1, 4, 9, 16]
>>> print(list3)
[9, 16]

列表表達式也可以遍歷元組生成新的列表(如list5),然而卻不能直接生成新的元組。比如list6實際上是個生成器表達式,不屬於列表表達式了。對於生成器表達式可以通過使用list6.__next__()方法一次一次取值。

>>> tuple1 = (1, 2, 3, 4)
>>> list5 = [ i*i for i in tuple1 ]
>>> print(list5)
[1, 4, 9, 16]
>>> list6 = ( i*i for i in tuple1 )
>>> print(list6)
<generator object <genexpr> at 0x03559E10>

下面我們來看下3個常考的關於列表表達式應用的例子。

1.統計字符串列表中每個字符串的長度

>>> words = ['abc','defg','I love python', 'Django']
>>> len_list = [ len(word) for word in words ]
>>> print(len_list)
[3, 4, 13, 6]
2. 利於列表表達式求兩個列表的交集。

>>> a = [ 3, 5, 6, 8]
>>> b = [ 5, 6]
>>> a_b = [ i for i in a if i in b]
>>> print(a_b)
[5, 6]
3. 打印出100以內所有十位數比個位數大1位的數字。

>>> num = [ n1*10+n2 for n1 in range(0, 10) 
        for n2 in range(0, 10) if n1 == n2+1 ]
>>> print(num)
[10, 21, 32, 43, 54, 65, 76, 87, 98]
 

匿名函數lambda函數

Lambda函數又稱匿名函數,也有人稱爲lambda表達式。顧名思義,匿名函數就是沒有名字的函數。函數沒有名字也行?當然可以啦。有些函數如果只是臨時一用,而且它的業務邏輯也很簡單時,就沒必要非給它取個名字不可。

 

lambda匿名函數的格式是 lambda 參數: 表達式。冒號前是參數,可以有多個,用逗號隔開,冒號右邊的爲表達式。其實lambda返回值是一個函數的地址,也就是函數對象。下面是個最簡單的lambda函數的例子。

>>> add = lambda x, y: x+y
>>> type(add)
<class 'function'>
>>> print(add(3,5))
8
既然是匿名函數,我們爲什麼還要給它取個叫add的名字?這的確是多次一舉。其實lambda最常用的還是和sorted, map、reduce、filter這些高級函數結合使用。我們再來看下2個使用lambda函數結合sorted方法排序的經典例子。一個按絕對值大小排序,一個按字符串長度排序。

>>> list1 = [ 5, -3, 1, 8, -4 ]
>>> list2 = sorted(list1, key=lambda x:abs(x))
>>> print(list2)
[1, -3, -4, 5, 8]

>>> list3 = [ 'to', 'python', 'ABC' ]
>>> list4 = sorted(list3, key=lambda x:len(x))
>>> print(list4)
['to', 'ABC', 'python']
下面是一道關於lambda函數的經典面試題。flist[0]能輸出什麼?這個主要考函數對象列表,千萬不要和列表表達式搞混了啊。答: flist[0]輸出的是函數對象。

>>> flist = [ lambda x:x*x for x in range(1, 3)]
>>> print(flist)
[<function <listcomp>.<lambda> at 0x03ADE2B8>, <function <listcomp>.<lambda> at 0x03ADE300>]
>>> flist[0]
<function <listcomp>.<lambda> at 0x03ADE2B8>
>>> flist[0](2)
4
 

zip函數

zip()函數來可以把2個或多個列表合併,並創建一個元組對的列表。元組對的數量以合併列表的最短長度爲準。python 3中zip方法合併列表後生成的是zip對象,使用list方法可以將其變成列表,使用dict方法可以將其變成字典。

>>> l1 = [ 1, 2, 3 ]
>>> l2 = [ 'x', 'y', 'z']
>>> l3 = [ 'x', 'y' ]
>>> zip(l1, l2)
<zip object at 0x031D6828>
>>> print(list(zip(l1, l2)))
[(1, 'x'), (2, 'y'), (3, 'z')]
>>> print(list(zip(l1, l3)))
[(1, 'x'), (2, 'y')]
>>> print(dict(zip(l1,l3)))
{1: 'x', 2: 'y'}
實際上zip方法支持所有可迭代對象(字符串、列表、元祖、字典), 而不僅僅是列表。利用這個特性,我們可以很容易創建各種字典,包括很複雜的字典。我們來看下面2個經典例子。注意zip對象支持直接遍歷哦,不需要先轉成list或dict哦。

>> > l1 = [1, 2, 3]
>> > str1 = "abc"
>> > print(dict(zip(l1, str1)))
{1: 'a', 2: 'b', 3: 'c'}
>> > name = ["John", "Jim", "Lucy"]
>> > year = [1983, 1985, 1995]
>> > birth_year = dict(zip(name, year))
>> > print(birth_year)
{'John': 1983, 'Jim': 1985, 'Lucy': 1995}
>> > for name, year in zip(name, year):
    print("{} - {}".format(name, year))

John - 1983
Jim - 1985
Lucy - 1995
利用zip(*some_list)方法可以實現元組列表的反向解壓,見下面案例。注意unzip只支持元組列表,不支持dict直接解壓哦。這也是面試常考內容。

>>> l1 = [("John", 1995), ("Lucy", 2000), ("Max", 1985)]
>>> name, year = zip(*l1)
>>> print(name)
('John', 'Lucy', 'Max')
>>> print(year)
(1995, 2000, 1985)
>>> l2 = dict(l1)
>>> print(l2)
{'John': 1995, 'Lucy': 2000, 'Max': 1985}
>>> name1, year1 = zip(*l2)
Traceback (most recent call last):
  File "<pyshell#6>", line 1, in <module>
    name1, year1 = zip(*l2)
ValueError: too many values to unpack (expected 2)
 

enumerate() 函數
enumerate() 函數用於將一個可遍歷的數據對象(如列表、元組或字符串)組合爲一個索引序列,同時列出數據和數據下標,常見用於for循環。一般只有需要同時用到索引index和value值的時候才用enumerate方法。注意直接使用enumerate方法生成是個enumerate對象,可以遍歷的。

>> > name = ["John", "Lucy", "Mary"]
>> > name1 = enumerate(name)
>> > print(name1)
< enumerate object at 0x030D0FA8 >
>> > for index, name in enumerate(name):
    print("{}-{}".format(index, name))

0 - John
1 - Lucy
2 - Mary
 

map函數

map函數是個非常有用的方法,其語法是 map(function, iterable, ...)。map方法可以接收函數作爲參數,並將其映射於列表的多個元素。python 2中返回列表,python 3中返回迭代器,需要使用list方法再生成列表。map函數不僅支持自定義的函數和lambda函數,還支持python自帶的函數。

 

下面是map函數應用一個最簡單的例子。該例子將計算平方的lambda函數映射於列表中的每個元素。

>>> l = map(lambda x: x ** 2, [1, 2, 3, 4, 5])
>>> print(l)
<map object at 0x03553790>
>>> print(list(l))
[1, 4, 9, 16, 25]
map函數還支持多個列表的映射和計算,如下所示。lambda函數中的x, y, z分別對應列表l1, l2和l3中的元素。計算後的生成的列表長度取決於各個列表的最短長度。

>>> l1 = [1, 2, 3]
>>> l2 = [4, 5, 6, 7]
>>> l3 = [8, 9]
>>> print(list(map(lambda x,y,z:x+y+z, l1, l2, l3)))
[13, 16]
我們下面來看下一道關於map函數的經典考題。我們有兩個字符串A和B,現在要統計字符串A中有多少個字符也在字符串B中可以找到。常規函數解法如下:

>>> strA = "aAAAbBCC"
>>> strB = "aA"
>>> def count1(str1, str2):
   a = 0
   for c in str1:
      if c in str2:
         a += 1
   return a

>>> count1(strA, strB)
4
使用map函數經典解法如下,是不是好簡單好牛叉的樣子?

>>> strA = "aAAAbBCC"
>>> strB = "aA"
>>> print(sum(map(strA.count, strB)))
4
我們現在來分析下上面這段代碼。python自帶的string.count(char)函數的作用是統計一個字符串string含有字符char的數量。在本例中strB相當於char的一個參數列表["a", "A"], map函數先統計strA中字符a的數量,再統計strA中字符A的數量,獲得列表[1, 3], 然後將它們相加,即可獲得字符串A中總共有多少字符可以在B中找到。

 

reduce函數

reduce() 函數會對參數序列中元素進行累積。該方法第一個參數必需是函數,而且傳入的函數必需要有2個參數,否則出現錯誤。該方法將一個數據集合(列表,元組等)中的所有數據進行下列操作:用傳給 reduce 中的函數 function先對集合中的第 1、2 個元素進行操作,得到的結果再與第三個數據用 function 函數運算,最後得到一個結果。

 

reduce函數很適合連續計算(比如列表求和或連乘計算階乘),經典代碼如下。

>>> from functools import reduce
>>> reduce(lambda x, y: x+y, [1,2,3,4,5])
15
>>> reduce(lambda x,y:x*y,range(1,5))
24
注意: python 3中reduce() 函數已經被從全局名字空間裏移除了,它現在被放置在 fucntools 模塊裏,如果想要使用它,則需要通過引入 functools 模塊來調用 reduce() 函數。

 

filter函數

Python的 filter() 函數用於過濾序列,過濾掉不符合條件的元素,返回由符合條件元素組成的新列表。該方法與map和reduce類似,第一個參數都是函數,作用於可以迭代的對象比如列表和元組,但不同的是filter方法傳入的函數是判斷性函數, 只有符合條件的列表元素纔會加入新的列表。Python 2中返回列表,python 3中返回filter對象,使用list方法可以轉化爲列表。

 

下面是使用filter方法打印出10以內偶數的經典代碼。只有滿足x % 2 == 0 的列表元素纔會加入新的列表。

>>> l = filter(lambda x: x % 2 == 0, range(10))
>>> print(l)
<filter object at 0x02C0BBF0>
>>> print(list(l))
[0, 2, 4, 6, 8]
下面這段代碼利用filter方法刪除字符串列表裏的空白字符串。只有滿足s and s.strip() = True的字符串纔會加入新的列表。

def not_empty(s):
    return s and s.strip()

list(filter(not_empty, ['A', '', 'B', None, 'C', '  ']))
# 結果: ['A', 'B', 'C']
最好一道是關於filter方法的經典面試題,利用filter方法篩選出一個列表中的素數。網上大多數代碼都不完美,一是不能對小於等於2的數(包括0, 1和2)做出準確判斷,二是使用了for i in range(2, n+1),做了很多無效循環。

>>> import math
>>> def isPrime(n):
       if n <= 1:
          return False
       elif n == 2:
          return True
       else:
          for i in range(2, int(math.sqrt(n))+1):
             if n%i == 0:
                return False
          return True

>>> l1 = [-1, 0, 2, 3, 6, 7, 8]
>>> l2 = list(filter(isPrime, l1))
>>> print(l2)
[2, 3, 7]
 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章