1. 程式人生 > >【python學習筆記】列表生成式和生成器

【python學習筆記】列表生成式和生成器

一、列表生成式

列表生成式即List Comprehensions,是Python內建的非常簡單卻強大的可以用來建立list的生成式。
列表生成式由包含一個表示式的括號組成,表示式後面跟隨一個for子句,之後可以有零或多個forif子句。結果是一個列表,由表示式依據其後面的forif子句上下文計算而來的結果構成。
舉個例子,要生成list[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]可以用list(range(1, 11))

>>> list(range(1, 11))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

但如果要生成[1x1, 2x2, 3x3, ..., 10x10]

怎麼做?方法一是迴圈:

>>> L = []
>>> for x in range(1, 11):
...    L.append(x * x)
...
>>> L
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

可以看到寫起來比較麻煩,那麼有沒有簡單的方法呢?有的,那就是執行列表生成式,如下:

>>> [x * x for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

for迴圈後面還可以加上if判斷,這樣我們就可以篩選出僅偶數的平方:

>>> [x * x for x in range(1, 11) if x % 2 == 0]
[4, 16, 36, 64, 100]

多元素的範例如下:

>>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

其等價於如下這個方法:

>>> combs = []
>>> for x in [1,2,3]:
...     for y in [3,1,
4]: ... if x != y: ... combs.append((x, y)) ... >>> combs [(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

一個實際應用方法:
列出一個資料夾下的所有檔案:

>>> import os # 匯入os模組
>>> [d for d in os.listdir('.')] # os.listdir可以列出檔案和目錄
['.emacs.d', '.ssh', '.Trash', 'Adlm', 'Applications', 'Desktop', 'Documents', 'Downloads', 'Library', 'Movies', 'Music', 'Pictures', 'Public', 'VirtualBox VMs', 'Workspace', 'XCode']

把一個list中所有的字串變成小寫:

>>> L = ['Hello', 'World', 'IBM', 'Apple']
>>> [s.lower() for s in L]
['hello', 'world', 'ibm', 'apple']

上面的例子都是一維的列表生成式,那麼如果是多維的呢,譬如要生成類似如下這樣一個列表:

>>> matrix = [
...     [1, 2, 3, 4],
...     [5, 6, 7, 8],
...     [9, 10, 1112],
... ]

可以使用如下這種方法

>>> [[row[i] for row in matrix] for i in range(4)]
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

二、生成器

通過列表生成式,我們可以直接建立一個列表。但是,受到記憶體限制,列表容量肯定是有限的。而且,建立一個包含100萬個元素的列表,不僅佔用很大的儲存空間,如果我們僅僅需要訪問前面幾個元素,那後面絕大多數元素佔用的空間都白白浪費了。

所以,如果列表元素可以按照某種演算法推算出來,那我們是否可以在迴圈的過程中不斷推算出後續的元素呢?這樣就不必建立完整的list,從而節省大量的空間。在Python中,這種一邊迴圈一邊計算的機制,稱為生成器:generator。

將列表生成式的[]改為()即可,譬如如下這種:

>>> L = [x * x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x * x for x in range(10))
>>> g
<generator object <genexpr> at 0x1022ef630>

此時要獲取其內容,需要呼叫next(g)方法,如下:

>>> next(g)
0
>>> next(g)
1
>>>

直到最後一個元素:

>>> next(g)
64
>>> next(g)
81
>>> next(g)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

可以看出來,generator儲存的是演算法,每次呼叫next(g),就計算出g的下一個元素的值,直到計算到最後一個元素,沒有更多的元素時,丟擲StopIteration的錯誤。
運用迭代可以更簡單的獲取內容:


>>> g = (x * x for x in range(10))
>>> for n in g:
...     print(n)
... 
0
1
4
9
16
25
36
49
64
81