之前介紹過清單推導式,清單推導式缺點:
1、建立清單推導式時就給他配置設定了記憶體空間,當清單很大時,會占用大量記憶體空間
2、我們可能隻用到清單中前幾個元素,後續才能用到其他元素,造成記憶體白白占用不釋放
解決:
1、使用生成器(generator),不用建立完整的list,一邊循環一邊建立
今天我們就來介紹一下生成器
1 建立生成器–清單推導式
[] 是清單推導式,() 是生成器
# 清單推導式
list1 = [x for x in range(10)]
print(type(list1))
# 生成器
generator1 = (x for x in range(10))
print(type(generator1))
運作結果:
<class 'list'>
<class 'generator'>
2 擷取生成器的值–next
# 擷取生成器的值:隻有在擷取值的時候才會配置設定每個值的記憶體空間
# 方式1:__next__()
print(generator1.__next__()) # 0
print(generator1.__next__()) # 1
print(generator1.__next__()) # 2
# 方式2:next()
print(next(generator1)) # 3
print(next(generator1)) # 4
print(next(generator1)) # StopIteration:當生成器中的元素被取完時,報錯
3 建立生成器–函數+yield
首先看函數func1
def func1():
n = 0
while True:
n += 1
func1是個死循環函數
用yield關鍵字
def func():
n = 0
while True:
n += 1
# yield功能 = return n + 暫停
# 當此次next調用時,遇到yield,則将yield後的值作為函數傳回值傳回,暫停在這
# 下次next調用時,從yield後繼續執行 --> print(f'n={n}') -> 再次進入到while循環
# 再次遇到yield,return n + 暫停
yield n
print(f'n={n}')
g = func() # 用debug模式檢視,g=func()時并未真正執行func函數,隻有在next時才進入func内部執行
print(type(g)) # <class 'generator'>
print(g.__next__())
print('===========')
print(next(g))
加了yield關鍵字,func()則變成了一個生成器generator,則可用__next__()或next()對生成器取值
運作結果:
===========
<class 'generator'>
===========
start
1
===========
n=1
2
4 yield + return
def func(length):
a = 0
while a<length:
yield a
a += 1
return '沒有更多資料了,循環結束' # 當生成器的值被取完時,報錯資訊 StopIteration的内容就是return中的内容
g = func(2)
print(next(g))
print(next(g))
print(next(g)) # StopIteration: 沒有更多資料了,循環結束
print(next(g)) # 不會被執行
5 擷取生成器的值–send
def func():
i = 0
while i < 5:
tmp = yield i
print(f'tmp={tmp}')
i += 1
g = func()
print('===========')
print(next(g))
print('===========')
print(next(g))
運作結果:
===========
0
===========
tmp=None
1
可以通過send方法給yield傳值
def func():
i = 0
while i < 5:
tmp = yield i
print(f'tmp={tmp}')
i += 1
g = func()
# 第一次必須要傳遞None
n0 = g.send(None)
print(f'send1: {n0}') # 遇到yield暫停,并return i=0 給n0
print('=========')
# 第二次調用,從tmp = yield i處執行,send的值tmp1傳遞給tmp
# 往下執行 print(f'tmp={tmp}') --> tmp=tmp1
# 往下執行 i+=1 --> i=1
# 往下執行再次進入while循環,遇到yield暫停,return i 給n1 --> n1=1
n1 = g.send('tmp1')
print(f'send2: {n1}')
print('=========')
n2 = g.send('tmp2')
print(f'send3: {n2}')
運作結果:
send1: 0
=========
tmp=tmp1
send2: 1
=========
tmp=tmp2
send3: 2
send的用途:需要生成元素,并且外界還可傳遞元素時
'''
next():在調用時生成器生成元素
send(value):調用時生成元素,還可以往生成器傳遞值
'''
def func():
i = 0
while i < 5:
tmp = yield i
for x in range(tmp):
print(f'調用時傳遞值------> x={x}')
i += 1
g = func()
g.send(None)
print(f'生成器生成的元素---> {g.send(2)}')
6 生成器應用
生成器可用在攜程中,交替調用函數時
def task1(n):
for i in range(n):
print(f'吃第{i}顆糖')
yield
def task2(n):
for i in range (n):
print (f'喝第{i}次水')
yield
g1 = task1(2)
g2 = task2(2)
while True:
try:
g1.__next__()
g2.__next__()
except:
break
運作結果:
吃第0顆糖
喝第0次水
吃第1顆糖
喝第1次水
總結
生成器生成方式:
1、清單推導式方式
g = (x for x in range(10))
2、函數+yield
def func():
yield
g = func()
生成器取值
1、next(generator) --> 每次調用都會産生新的元素,當元素調用完,報錯StopIteration
2、生成器内部方法
g.next()
g.send(value)