【百尺竿頭,更進一步學Python】Python進階課程——生成器
學習了迭代器以后,今天我們來講它的好兄弟生成器.在學習生成器之前,我們先對學過的迭代器進行一次鞏固.
迭代器(鞏固)
- 我們都知道利用迭代器,可以在每次迭代獲取資料(通過next方法)時,按照特點的規律進行生成,但是我們在實作一個迭代器的時候,關于當前迭代到的狀態需要我們自己記錄,進而才能根據當前狀態生成下一個資料,為了達到記錄當前狀態,并配合next方法進行迭代使用,這種方式是我們提前將資料創建好了以后才進行的.
- 在Python中除了for遍歷語法在in之后需要使用迭代器,其他的一些串列型別轉換(list,tuple)等也需要使用迭代器
生成器
什么是生成器
- 生成器是一種特殊的迭代器
創建生成器
創建生成器有兩種方式:
-
創建生成器,只需要把一個串列推導式的[]改成()即可
p = (i for i in range(1000)) print(p) -
創建生成器,只需要在一個函式中使用yield陳述句,那么這個函式也不再叫函式,就叫生成器,yield在函式里面出現,這就是一個生成器模板.(我們運用以前學過的案例"水仙花數(完數)"來進行講解)
def create_shui(startnum, endnum): while startnum < endnum: ge = startnum % 10 shi = (startnum % 100) // 10 bai = (startnum % 1000) // 100 qian = startnum // 1000 if startnum == ge ** 3 + shi ** 3 + bai ** 3 + qian ** 3: yield startnum startnum += 1 if __name__ == "__main__": ret = create_shui(1, 1000) print(ret) for item in ret: print(item)
總結:
- 只要在呼叫一個函式時發現函式中有yield,那么這就不是呼叫函式,而是創建了一個生成器物件
yield關鍵字
def create_shui(startnum, endnum):
print("開始...")
while startnum < endnum:
print("...1...")
yield startnum
print("...2...")
startnum += 1
print("...3...")
if __name__ == "__main__":
# ret本質是一個生成器物件,生成器的本質是一個迭代器
ret = create_shui(1, 5)
res = next(ret)
print(res)
# 執行的最后結果是:
# 開始...
# ...1...
# 1
從而我們可以得出yield關鍵字的本質.
yield的本質
- 將該函式標記為生成器,在呼叫有yield關鍵字的函式時,目標生成一個生成器物件,對這個生成器物件進行迭代時,每次的迭代都只執行到yield這塊,而yield后面的資料是本次迭代的產物資料,只要不進行下一次迭代,代碼流程一致監聽在yield這塊
- 保存當前的運行狀態,然后暫停執行,也就是將生成器掛起
- yield關鍵字后面的運算式作為值回傳
注意:
- python3中的生成器里面可以使用return回傳最終運行的回傳值,
- 而在Python2中生成器里面絕對不允許出現return的回傳值(Python2中可以使用return但是return后面絕對不能有資料運算式)
生成器的喚醒
使用next()函式
- 我們可以對生成器使用next()函式讓生成器開始迭代一次,也就是喚醒一次生成器
實體:
def create_shui(startnum, endnum):
print("開始...")
while startnum < endnum:
print("...1...")
yield startnum
print("...2...")
startnum += 1
print("...3...")
if __name__ == "__main__":
# ret本質是一個生成器物件,生成器的本質是一個迭代器
ret = create_shui(1, 5)
res = next(ret)
print(res)
# 再來一次
res = next(ret)
print(res)
# 再來一次
res = next(ret)
print(res)
# 執行到最后的結果是
# 開始...
# ...1...
# 1
# ...2...
# ...3...
# ...1...
# 2
# ...2...
# ...3...
# ...1...
# 3
使用send喚醒
- send一般不會放到第一次啟動生成器,如果非要這樣做,那么選擇傳遞一個None
- send里面的引數會當做資訊傳遞給yield當做yield的結果,然后通過一個變數可以接收這個結果
- send的結果是下一呼叫yield時,yield后面的值
實體:
def create_shui(startnum, endnum):
print("開始...")
while startnum < endnum:
print("...1...")
res = yield startnum
print(res)
print("...2...")
startnum += 1
print("...3...")
else:
return "結束..."
if __name__ == "__main__":
ret = create_shui(1, 5)
print(next(ret))
# send一般不會放到第一次啟動生成器
# 如果非要這樣做,那么選擇傳遞一個None
# print(ret.send(None))
print(ret.send("123"))
print(ret.send("456"))
# 最后的執行結果是:
# 開始...
# ...1...
# 1
# 123
# ...2...
# ...3...
# ...1...
# 2
# 456
# ...2...
# ...3...
# ...1...
# 3
并發執行的模擬
import time
def upload():
while True:
time.sleep(0.5)
print("上傳檔案")
yield
def download():
while True:
time.sleep(1)
print("下載檔案")
yield
def main():
ret1 = upload()
ret2 = download()
i = 1
while i <4:
next(ret2)
next(ret1)
i += 1
-
這個程序其實就是先讓ret2先運行一會,當ret2中遇到了yield,讓ret1運行
-
然后當ret1中遇到了yield再讓ret2運行,這樣就實作了/ret2/ret1/ret2/ret1/…交替運行,最終實作了所謂的并發
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/4810.html
標籤:其他
