前言
為了方便追小說更新擼了一個基於scrapy的爬蟲。在實現過程中使用到了 yield,網上對其的文字描述都很難讓人理解。通過Debug程式碼才瞭解呼叫順序,進而理解了它使用方法。
概念
理解yield作用
我們可以用一個等式來形容其作用:
yeild 函式 = return 生成器(generator) 用同步方式寫非同步
什麼是生成器
生成器 = 可迭代的函式
正常情況下我們可以這樣迭代一個列表
# encoding:UTF-8
def call(i):
return i * 2
array = [call(0),call(1),call(2)]
for i in array:
print(i, ",")
複製程式碼
列印結果:
0 ,
2 ,
4 ,
複製程式碼
以上的程式碼,適合在資料量小的情況下執行,假如在海量資料的場景下,這樣的寫法將對記憶體造成很大的壓力。因為列表內所有資料都同時載入在記憶體中。
而python的生成器則完美的解決了這一問題,它不需要將所有的值同時載入,它只提供了一個生成資料的方式, 而且它是可迭代
# encoding:UTF-8
def call(i):
return i * 2
def generator(n):
for i in range(n):
yield call(i)
for i in generator(3):
print(i, ",")
複製程式碼
列印結果:
0 ,
2 ,
4 ,
複製程式碼
可以看到 generator 做的事情很簡單:迴圈生成了0到2相對應的值。而yield的作用就是提供生成器給外部。
這樣做的好處就在於每次值都是按需生成的,且生成完不會停駐在記憶體中。
呼叫順序
值得一提的就是yield的呼叫順序也是很清奇的,它的呼叫順序和我們常見的簡單的自上而下。 我們在以上的demo中加入幾行print
# encoding:UTF-8
def call(i):
return i * 2
def generator(n):
for i in range(n):
yield call(i)
print("generate i=", i)
print("end.")
for i in generator(3):
print(i, ",")
複製程式碼
在我們的預期中,我們的預期執行順序是
這樣的話執行結果應該是這樣:generate i= 0
0 ,
generate i= 1
2 ,
generate i= 2
4 ,
end.
複製程式碼
而它的結果執行結果卻是這樣:
0 ,
generate i= 0
2 ,
generate i= 1
4 ,
generate i= 2
end.
複製程式碼
那麼意味著它的執行順序是這樣:
debug後也發現的確如此。可以發現,只要執行到yield關鍵字都會先return,在外層執行完畢後,再執行yield之後下一條指令。
啟發
當我們處理資料量大的事物的時候,可以效仿關鍵字yield這樣的思路:持有索引或者其他可檢索的id,在需要的時候再去通過構造器或者其他工具獲取