【python學習筆記】列表生成式和生成器
一、列表生成式
列表生成式即List Comprehensions,是Python內建的非常簡單卻強大的可以用來建立list的生成式。
列表生成式由包含一個表示式的括號組成,表示式後面跟隨一個for
子句,之後可以有零或多個for
或if
子句。結果是一個列表,由表示式依據其後面的for
和if
子句上下文計算而來的結果構成。
舉個例子,要生成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, 11,12],
... ]
可以使用如下這種方法
>>> [[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