這個概念較難理解,我們可以通過解答以下幾個問題來掌握它。
本節會用到
可迭代對象
及列表生成器
知識,建議先看下之前的文章進行了解。【python實用特性】- 迭代、可迭代對象、迭代器
【python實用特性】- 列表生成式
1、什麼是生成器?
生成器: 生成
,很好理解,創建一個對象的意思。那器
是什麼呢?器可以理解爲工具的意思,所以生成器,就是按照一定規則,創建某種對象的工具。
2、爲何要用生成器?有何好處?
舉個簡單的例子,我們要創建一個100個元素的列表。很簡單,list(range(100))
,那如果要創建一個100萬、1000萬元素的列表呢?
誠然,我們依舊可以使用list(range(10000*100)
、list(range(10000*1000)
來達到目的。
但這樣會帶來幾個問題:
- 變量存儲於內存中,所以列表長度受限於內存大小,是有限的
- 列表需要將所有元素一次性創建完成後才能操作,數據過大時,內存使用會瞬間升幅,存在內存不足風險
- 大多數時候只需要用到部分元素,這就造成了內存空間的浪費
爲了解決以上問題,於是引入了一種不用一次性全部生成,可以按需逐次獲取數據的方案——生成器
所以使用生成器的好處也很明顯:
(1)不用一次性生成所有數據,能有效節約內存
(2)可以按需逐次獲取數據,不會造成內存空間的浪費
3、python中如何實現生成器?
一般來說,python中的生成器有兩種實現方式。
-
普通實現
將列表生成式的[]
改爲()
即可,實例如下:g = (x for x in range(10)) print(type(g)) #type查看g的類型 from typing import Iterable print(isinstance(g,Iterable)) #判斷是否是可迭代對象
輸出:
<class 'generator'> True
可見,此時的
g
便是一個生成器,同時也是一個可迭代對象!
這意味着我們可以使用next(g)
進行 取值 -
yield實現
實例如下,打印0~2#自然數生成器 def getN(N): n = 0 while n<N: yield n #使用yield 返回數據 n+=1 g = getN(3) print(next(g)) print(next(g)) print(next(g))
輸出:
0 1 2
如上,我們在函數
getN(N)
中使用了yield
關鍵字返回數據,所以這個函數就變成了一個生成器。
小結: 若一個函數中使用了yield
返回數據,那這個函數就是一個生成器。
4、yield生成器的運行機制
通過上面的學習,我們已經知道如何去構造一個生成器。那它是如何運行的呢?又爲何說它是逐次獲取數據呢?
先看一個例子
def test():
print('返回第一個數據:')
yield 'data1'
print('返回第二個數據:')
yield 'data2'
print('返回第三個數據:')
yield 'data3'
g = test()
print(next(g))
輸出:
返回第一個數據:
data1
可見,有三個yield 返回,但因爲我們只使用了一次 next()
函數,所以只打印了data1
,第一個yield
返回的數據。
那調用兩次呢?
print(next(g))
print(next(g))
輸出:
返回第一個數據:
data1
返回第二個數據:
data2
小結: 調用一次 next()
函數,便執行一次生成器,遇到yield
關鍵字後,將數據返回。之後生成器暫停運行,保存當前的運行狀態,等待下一個next()
調用。
5、使用循環來迭代生成器
上面的例子中,我們都是通過next()
函數來取值。這種方式當數據量大了之後,就會變得很麻煩。所以需要使用更高效的方法,而因爲生成器也是一個可迭代對象,所以我們可以使用循環來進行迭代。
打印0~2的實例改寫如下:
-
while循環
def getN(N): n = 0 while n<N: yield n n+=1 g = getN(10) while True: try: print(next(g),end='\t') except StopIteration: break #跳出循環
注:使用
while
循環實現時,要用try...except
捕獲StopIterator
異常。 -
for循環
def getN(N): n = 0 while n<N: yield n n+=1 g = getN(3) for data in g: print(data)
注:
for
循環會自動調用next()
函數,且捕獲異常。推薦使用。