Python 列表生成式和生成器?可迭代和迭代器?
列表生成式和生成器?
列表生成式,就是一個能夠生成列表的推導式,用[]括起來,裏面是一個包含for循環的推導式,形式一般如下:
[x * x for x in range(1, 11) if x % 2 == 0]
意思是從1-10遍歷,選取偶數數字然後平方組成新的列表。
生成器,generator,兩種寫法,第一種把上面的列表生成式的中括號[]改成小括號(),如下:
g = (x * x for x in range(10))
需要注意這樣得到的不是一個列表也不是元組,也不是1-10每個數平方後的數據,而是一個生成器對象,它又麼用呢?
使用列表生成式是一下子把所有元素都計算出來,比如說我們最終要100萬個元素,列表生成式一下子計算就得到了100萬個元素,但是就目前而言我只用到前10個後面的元素以後纔可能用,那一下子計算出100萬個又慢有佔空間。
所以生成器就可以按需計算,即我們只需要告訴它計算推導的規則就行,想要得到下一個元素,我們就調用next()方法按照規則計算獲取下一個元素,當然也可以用for循環獲取前n個元素,所以它有一種惰性計算的性質。
比如上面代碼中的生成器,可以使用next()方法不斷獲取下一個元素:
g = (x * x for x in range(10))
next(g) # 0
next(g) # 1
next(g) # 4
也可以使用for循環:
g = (x * x for x in range(10))
for n in g:
print(n)
# 0
# 1
# 4
# 9
...
# 81
第二種方式就是通過函數告訴它計算規則,但是函數畢竟是函數,怎麼變成生成器呢,只要每次計算出數據用yield關鍵字返回就行了,比如計算斐波那契數量:
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b # 計算出b並返回,下次執行從上次終止的地方開始而不是重新開始
a, b = b, a + b
n = n + 1
return 'done'
也是可以通過next()或者for循環去遍歷,比如使用next()方法:
g = fib(6) # 創建一個生成6個斐波那契數的生成器
next(g)
# 1
next(g)
# 1
next(g)
# 2
next(g)
# 3
next(g)
# 5
next(g)
# 8
next(g)
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# StopIteration
發現每次執行next()方法,它是從上次停止的地方繼續執行,而不會像函數一樣從頭開始,最後結束如果繼續調用next()方法會返回StopIteration
異常。
使用for循環同理:
for n in fib(6):
print(n)
# 1
# 1
# 2
# 3
# 5
# 8
可迭代和迭代器?
可以直接作用於for
循環的數據類型有以下幾種:
一類是集合數據類型,如list
、tuple
、dict
、set
、str
等;
一類是generator
,包括生成器和帶yield
的generator function。
凡是可作用於for
循環的對象都是可迭代的,叫Iterable
類型;
凡是可作用於next()
函數的對象都是迭代器,叫Iterator
類型,它們表示一個惰性計算的序列;
集合數據類型如list
、dict
、str
等是Iterable
但不是Iterator
,不過可以通過iter()
函數獲得一個Iterator
對象。
Python的Iterator
對象也就是迭代器,表示的是一個數據流,Iterator對象可以被next()
函數調用並不斷返回下一個數據,直到沒有數據時拋出StopIteration
錯誤。可以把這個數據流看做是一個有序序列,但我們卻不能提前知道序列的長度,只能不斷通過next()
函數實現按需計算下一個數據,所以Iterator
的計算是惰性的,只有在需要返回下一個數據時它纔會計算。
Iterator
甚至可以表示一個無限大的數據流,例如全體自然數。而使用list是永遠不可能存儲全體自然數的。