生成器:含有yield的函數。(無需藉助類就能實現)
功能:函數執行過程中可中斷、可重開、可暫停、可續傳
爲什麼要用生成器? 解決內存佔用問題,看最後一段代碼。
原理:是基於迭代器來實現(既然生成器是一個迭代器,它可以被用在for 循環中),內部會自動創建__iter__()和__next__()方法。
運行規則:
遇到yield,程序暫停,並返回值,下次還從該位置運行
對比return則是程序停止,並返回值
基本語法:
def myGen():
print('生成器執行')
yield 1
yield 2
使用方法1:
>>> my = myGen()
>>> next(my)
生成器執行
1
>>> next(my)
2
>>> next(my)
錯誤:StopIteration
使用方法2:
通過迭代器可知,for循環可以捕獲StopIteration,並結束
>>> for i in myGen():
print(i)
生成器執行
1
2
生成器推導式:
e = (i for i in range(5)) # e 也是一個生成器
next(e)
0
next(e)
1
for each in e:
print(each)
2
3
4
生成器的另一種結構:
def foo():
while True:
res = yield 4
print("res:",res)
g = foo() # 得到了生成器
print(next(g)) # 開始運行生成器g,第一次返回值爲4,此時生成器停止
print(next(g)) # 第二次運行:由於4被返回。所以並沒有賦值給res,res爲None,返回4
# 運行結果
4
res: None
4
代碼解析:
g = foo() :
g得到了生成器
print(next(g)) :
開始運行生成器g,第一次返回值爲4,此時生成器停止
print(next(g)) :
第二次運行:由於4被返回。所以並沒有賦值給res,res爲None,返回4
注意:和一般的賦值語句不同,res的值並不是通過yield後面的值賦值的吼
(因爲後面的值被返回了,怎麼賦?怎麼賦?沒法賦!沒法賦!)
那麼res的值時誰賦予的呢?
我們再來看一個小栗子:
def foo():
while True:
res = yield 4
print("res:",res)
g = foo() # 得到了生成器
print(next(g)) # 開始運行生成器g,第一次返回值爲4,此時生成器停止
# 以上:和之前一樣
print(g.send(7)) # 生成器g 的send()方法,從外部把7送到了生成器內部,賦值給了res
# 運行結果
4
res: 7
4
代碼解析:
print(g.send(7)) :
生成器g的send()方法: 從外部把7送到了生成器內部,賦值給了res
由此可見:res的值是通過send()方法,從外部傳遞的
調用send()與使用next()有點像,都會使生成器進行下一次運行。不同的是send()可以往裏傳入一個值給res
小栗子:
10 以內的素數之和是:2 + 3 + 5 + 7 = 17
那麼請編寫程序,計算 2000000 以內的素數之和
如果先把所有的素數找到存在列表裏,再累加求和,則佔用太大內存可能溢出
用生成器:一次找到一個素數,找一個加一個,不佔用內存
import math
# 判斷是否爲素數
def is_prime(number):
if number > 1:
if number == 2:
return True
if number % 2 == 0:
return False
for current in range(3, int(math.sqrt(number) + 1), 2):
if number % current == 0:
return False
return True
return False
# 生成器函數
def get_primes(number):
while number:
if is_prime(number):
yield number
number -= 1
a = 0
for each in get_primes(2000000):
a += each # 每次產生一個值累加
print(a) # 142913828922
如果你感覺對你有幫助,你的讚賞是對我最大的支持!