通過列表生成式,我們可以直接建立乙個列表。但是,受記憶體限制,列表的容量也是有限的,當我們建立乙個包含100w個元素的列表,不僅占用記憶體空間比較多,而且假如我們只需要訪問前幾個元素,那麼後面絕大部分元素占用的空間都浪費了。
如果列表元素可以按照某種演算法推算出來,那我們是否可以在迴圈使用的過程中不斷推算後續的元素呢,這樣就不必建立完整的list,浪費空間。
在python中,這種一邊迴圈一遍計算的機制,稱為生成器(generator)。
首先,來看一下列表的建立:
l = [x for x in range(8)]
print(l)
參照上面**,只要做如下改動,就建立乙個生成器:
# 01. 將列表生成式中的改為()
g = (x for x in range(3))
print(g)
列印結果為:
object
at 0x102967ca8>
如上,就建立了乙個生成器物件。
直接列印g相當於輸出的是物件位址,那麼該如何取其中的元素呢?
通過next(g)或g.next()方式進行取值
# 列印g的值,通過next()函式 獲取生成器的下乙個返回值
print(g.__next__())
print(next(g))
print(next(g))
print(next(g))
print(next(g))
輸出為:
012
traceback (most recent call
last):
file "/users/***/project/pycharmprojects/**/generator_test.py", line 11, in
print(next(g))
stopiteration
可以看到,這樣可以取到生成器的值,不過當角標越界後,會跑出stopiteration異常,可以通過監聽次異常來完成取值。
try:
print(g.__next__())
print(next(g))
print(next(g))
print(next(g))
except stopiteration as e:
print('取值完成')
輸出為:
012
取值完成
通過for迴圈完成取值
for x in g:
print(x)
同樣可以完成對g的取值
只要函式中有yield關鍵字,該函式就不是普通函式了,而是乙個生成器物件。如果再通過函式名直接呼叫,是無效的。
def
create_num2
(number):
a, b = 0, 1
for x in range(number):
yield b
a, b = b, a + b
通過**,看一下生成器的呼叫過程。
def
create_num2
(number):
print('create_num2 start')
a, b = 0, 1
for x in range(number):
# 遇到yield程式暫停,同時把後面的值返回
# 如果再次用next()進行呼叫,就會從上次yiled暫停的地方繼續執行
print('....1....')
yield b
print('....2....')
a, b = b, a + b
print('....3....')
print('create_num2 stop')
a = create_num2(10)
print('*' * 10 + '第一次呼叫yiled物件' + '*' * 10)
print(next(a))
print('*' * 10 + '第二次呼叫yiled物件' + '*' * 10)
print(next(a))
輸出結果為:
*****
*****第一次呼叫yiled物件*****
*****
create_num2 start
....1....
1*****
*****第二次呼叫yiled物件*****
*****
....2....
....3....
....1....
1
執行流程:
1. 執行 a = create_num2(10),建立生成器物件a
2. 當呼叫next(a)的時候,才會執行到函式中,依次列印 start …1…
3. 當執行到yield b時,會將程式暫停,同時把後面的值(b)返回
4. 接收b的返回,此時為1,so 列印1
5. 再次執行next(a)的時候,會從上次yield暫停的地方繼續執行,所以先列印…2… …3…,然後再次進入for迴圈,列印…1…,又遇到yield,暫停中,同時返回b的值
6. 接收b的值,並列印
在呼叫生成器函式時,也可以對其進行傳值,如下:
# 定義乙個yield物件,並且增加temp = yield i
deftest
(): i = 0
while i < 5:
temp = yield i
print(temp)
i += 1
t = test()
# 首次呼叫
print(t.__next__())
# 第二次呼叫
print(t.__next__())
# 第三次呼叫
print(t.send('python'))
輸出為:
0
none
1python
2
執行流程:
1. 首次呼叫,因為temp = yield i為賦值語句,先執行右邊**,當執行到yield時,暫停執行,並把i返回回去,so ,列印 0
2. 第二次呼叫,從yield i開始,可以把yiedl i作為乙個**塊,賦值給temp,此時沒傳值,所以列印為none,其他正常執行
3. 第三次呼叫,通過send方法,傳遞引數,這時候相當於把引數賦值給temp,所以列印結果為python,其他流程正常執行
ps:在第一次呼叫生成器的時候不能傳參,否則會跑出異常,如下:
typeerror: can't send non-none value to a just-started generator
首次必須先呼叫next()或send(none)。
生成器是這樣乙個函式,它記住上一次返回時函式體中的位置。對生成器的第二次(n)呼叫跳轉至該函式中間,而上次呼叫的所有區域性變數都保持不變
生成器特點
節約記憶體
迭代到下一次的呼叫時,所使用的引數都是第一次所保留下的,也就是說,在整個所有函式呼叫的引數都是第一次呼叫時保留的,而不是新建立的。
python 生成器作用 Python生成器
生成器介紹 在函式內部包含yield關鍵字,那麼該函式執行的結果是生成器,生成器就是迭代器。生成器的功能 把函式結果做成迭代器 以一種優雅的方式封裝好iter,next 提供了一種自己定義迭代器的方式。使用生成器建立乙個迭代器 def a print a yield 11 使用yield,執行後返回...
python生成器好處 Python生成器筆記
python中三大器有迭代器,生成器,裝飾器,本文主要講述生成器。主要從生成器的概念,本質,以及yield關鍵字的使用執行過程。本質 生成器是一類特殊的迭代器,使用了yield關鍵字的函式不再是函式,而是生成器。使用了yield的函式就是生成器 1.yield關鍵字有兩點作用 1.1 yield語句...
python生成器函式 Python 生成器函式
一 生成器 生成器指的是生成器物件,可由生成器表示式得到,也可使用 yield 關鍵字得到乙個生成器函式,呼叫這個函式得到乙個生成器物件 生成器物件,是乙個可迭代物件,是乙個迭代器 生成器物件,是延遲計算 惰性求值的 1.1 生成器函式 函式體重包含 yield 語句的函式,就是生成器函式,呼叫後返...