Python推導式(列表推導式、元組推導式、字典推導式和集合推導式)詳解

2020-07-16 10:05:04
推導式(又稱解析器),是 Python 獨有的一種特性。使用推導式可以快速生成列表、元組、字典以及集合型別的資料,因此推導式又可細分為列表推導式、元組推導式、字典推導式以及集合推導式。

Python列表推導式

列表推導式可以利用 range 區間、元組、列表、字典和集合等資料型別,快速生成一個滿足指定需求的列表。

列表推導式的語法格式如下:

[表示式 for 疊代變數 in 可疊代物件 [if 條件表示式] ]

此格式中,[if 條件表示式] 不是必須的,可以使用,也可以省略。

通過列表推導式的語法格式,明顯會感覺到它和 for 迴圈存在某些關聯。其實,除去 [if 條件表示式] 部分,其餘各部分的含義以及執行順序和 for 迴圈是完全一樣的(表示式其實就是 for 迴圈中的迴圈體),即它的執行順序如下所示:

for 疊代變數 in 可疊代物件
    表示式

初學者可以這樣認為,它只是對 for 迴圈語句的格式做了一下簡單的變形,並用 [] 括起來而已,只不過最大的不同之處在於,列表推導式最終會將迴圈過程中,計算表示式得到的一系列值組成一個列表。

例如如下程式碼(程式一):
a_range = range(10)
# 對a_range執行for表示式
a_list = [x * x for x in a_range]
# a_list集合包含10個元素
print(a_list)
上面程式碼的第 3 行會對 a_range 執行迭代,由於 a_range 相當於包含 10 個元素,因此程式生成的 a_list 同樣包含 10 個元素,且每個元素都是 a_range 中每個元素的平方(由表示式 x*x 控制)。

執行上面程式碼,可以看到如下輸出結果:

[0 , 1 , 4 , 9 , 16 , 25 , 36 , 49 , 64, 81]


不僅如此,我們還可以在列表推導式中新增 if 條件語句,這樣列表推導式將只疊代那些符合條件的元素。例如如下程式碼:
b_list = [x * x for x in a_range if x % 2 == 0]
# a_list集合包含5個元素
print(b_list)
第一行程式碼與程式一中第 3 行程式碼大致相同,只是為這裡給列表推導式增加了 if 條件語句,這會導致推導式只處理 range 區間的偶數,因此程式生成的 b_list 只包含 5 個元素。

執行上面程式碼,可以看到如下輸出結果:

[0 ,4 , 16, 36, 64]


另外,以上所看到的列表推導式都只有一個迴圈,實際上它可使用多個迴圈,就像巢狀迴圈一樣。例如如下程式碼:
d_list = [(x, y) for x in range(5) for y in range(4)]
# d_list列表包含20個元素
print(d_list)
上面程式碼中,x 是遍歷 range(5) 的疊代變數(計數器),因此該 x 可疊代 5 次;y 是遍歷 range(4) 的計數器,因此該 y 可疊代 4 次。因此,該(x,y)表示式一共會疊代 20 次。上面的 for 表示式相當於如下巢狀迴圈:
dd_list = []
for x in range(5):
    for y in range(4):
        dd_list.append((x, y))

執行上面程式碼,可以看到如下輸出結果:

[(0, 0), (0, 1), (0, 2), (0, 3), (1, 0), (1, 1), (1, 2), (1, 3), (2, 0), (2, 1), (2, 2), (2, 3), (3, 0), (3, 1), (3, 2), (3, 3), (4, 0), (4, 1), (4, 2), (4, 3)]


當然,也支援類似於三層巢狀的 for 表示式,例如如下程式碼:
e_list = [[x, y, z] for x in range(5) for y in range(4) for z in range(6)]
# e_list列表包含120個元素
print(e_list)
對於包含多個迴圈的 for 表示式,同樣可指定 if 條件。假如我們有一個需求:程式要將兩個列表中的數值按“能否整除”的關係配對在一起。比如 src_a 列表中包含 30,src_b 列表中包含 5,其中 30 可以整除 5,那麼就將 30 和 5 配對在一起。對於上面的需求使用 for 表示式來實現非常簡單,例如如下程式碼:
src_a = [30, 12, 66, 34, 39, 78, 36, 57, 121]
src_b = [3, 5, 7, 11]
# 只要y能整除x,就將它們配對在一起
result = [(x, y) for x in src_b for y in src_a if y % x == 0]
print(result)
執行上面程式碼,可以看到如下輸出結果:

[(3, 30), (3, 12), (3, 66), (3, 39), (3, 78), (3, 36), (3, 57), (5, 30), (11, 66), (11, 121)]

Python元組推導式

元組推導式可以利用 range 區間、元組、列表、字典和集合等資料型別,快速生成一個滿足指定需求的元組。

元組推導式的語法格式如下:

(表示式 for 疊代變數 in 可疊代物件 [if 條件表示式] )

其中,用 [] 括起來的部分,可以使用,也可以省略。

通過和列表推導式做對比,你會發現,除了元組推導式是用 () 圓括號將各部分括起來,而列表推導式用的是 [],其它完全相同。不僅如此,元組推導式和列表推導式的用法也完全相同。

例如,我們可以使用下面的程式碼生成一個包含數位 1~9 的元組:
a = (x for x in range(1,10))
print(a)
執行結果為:

<generator object <genexpr> at 0x0000020BAD136620>


從上面的執行結果可以看出,使用元組推導式生成的結果並不是一個元組,而是一個生成器物件(後續會介紹),這一點和列表推導式是不同的。

如果我們想要使用元組推導式獲得新元組或新元組中的元素,有以下三種方式:
  1. 使用 tuple() 函數,可以直接將生成器物件轉換成元組,例如:
    a = (x for x in range(1,10))
    print(tuple(a))
    執行結果為:
    (1, 2, 3, 4, 5, 6, 7, 8, 9)
  2. 直接使用 for 迴圈遍歷生成器物件,可以獲得各個元素,例如:
    a = (x for x in range(1,10))
    for i in a:
        print(i,end=' ')
    print(tuple(a))
    執行結果為:

    1 2 3 4 5 6 7 8 9 ()

  3. 使用 __next__() 方法遍歷生成器物件,也可以獲得各個元素,例如:
    a = (x for x in range(3))
    print(a.__next__())
    print(a.__next__())
    print(a.__next__())
    a = tuple(a)
    print("轉換後的元組:",a)
    執行結果為:

    0
    1
    2
    轉換後的元組: ()

注意,無論是使用 for 迴圈遍歷生成器物件,還是使用 __next__() 方法遍歷生成器物件,遍歷後原生成器物件將不復存在,這就是遍歷後轉換原生成器物件卻得到空元組的原因。

Python字典推導式

Python 中,使用字典推導式可以藉助列表、元組、字典、集合以及 range 區間,快速生成符合需求的字典。

字典推導式的語法格式如下:

{表示式 for 疊代變數 in 可疊代物件 [if 條件表示式]}

其中,用 [] 括起來的部分,可以使用,也可以省略。

可以看到,和其它推導式的語法格式相比,唯一不同在於,字典推導式用的是大括號{}。

【例 1】
listdemo = ['C語言中文網','c.biancheng.net']
#將列表中各字串值為鍵,各字串的長度為值,組成鍵值對
newdict = {key:len(key) for key in listdemo}
print(newdict)
執行結果為:

{'C語言中文網': 6, 'c.biancheng.net': 15}


【例 2】交換現有字典中各鍵值對的鍵和值。
olddict={'C語言中文網': 6, 'c.biancheng.net': 15}
newdict = {v: k for k, v in olddict.items()}
print(newdict)
執行結果為:

{6: 'C語言中文網', 15: 'c.biancheng.net'}


【例 3】使用 if 表示式篩選符合條件的鍵值對。
olddict={'C語言中文網': 6, 'c.biancheng.net': 15}
newdict = {v: k for k, v in olddict.items() if v>10}
print(newdict)
執行結果為:

{15: 'c.biancheng.net'}

Python集合推導式

Python中,使用集合推導式可以藉助列表、元組、字典、集合以及 range 區間,快速生成符合需求的集合。

集合推導式的語法格式和字典推導式完全相同,如下所示:

{ 表示式 for 疊代變數 in 可疊代物件 [if 條件表示式] }

其中,用 [] 括起來的部分,可以使用,也可以省略。

有讀者可能會問,集合推導式和字典推導式的格式完全相同,那麼給定一個類似的推導式,如何判斷是哪種推導式呢?最簡單直接的方式,就是根據表示式進行判斷,如果表示式以鍵值對(key:value)的形式,則證明此推導式是字典推導式;反之,則是集合推導式。

【例 1】
setnew = {i**2 for i in range(3)}
print(setnew)
執行結果為:

{0, 1, 4}


【例 2】既然生成的是集合,那麼其儲存的元素必須是唯一的。
tupledemo = (1,1,2,3,4,5,6,6)
setnew = {x**2 for x in tupledemo if x%2==0}
print(setnew)
執行結果為:

{16, 4, 36}


【例 3】
dictdemo = {'1':1,'2':2,'3':3}
setnew = {x for x in dictdemo.keys()}
print(setnew)
執行結果為:

{'2', '1', '3'}