今天在跟著ReccaChao大的鐵人文章學習([Day 15] 終於講到 6.0 的改變了!來看 Laravel LazyCollection),發現到一個沒看過的語法[yield]、[Generator],馬上丟去餵狗。
先說說我對他倆的理解好了,yield必須在function內作用,當使用他接值時不需要return,yield會幫你處理好,且記憶體還不會被吃住,當使用此function時,就可以使用PHP內建Generator物件的一些method操作,這個物件操作還蠻像陣列的。
用yield只是省了記憶體,後續還是會把資料做處理,所以記憶體只是省在初始建立資料時,我還是習慣操作陣列,而且普遍很少看到有人使用yield(可能是我見識淺薄)。
以上是我的初步了解,如果以上理解有誤,歡迎提出。
想請教各位大大們,常用yield嗎 ? 通常會在哪個使用情境下使用呢?
PHP我不熟,不過跟python的yield應該是一樣的東西。
我們來看一下下面2個費氏函數(你把python的list當成php的array就行):
import sys
def fibY(n):
i, a, b = 1, 1, 1
while i <= n:
if i <= 2:
yield 1
else:
a, b = b, a+b
yield b
i += 1
def fibL(n):
res = []
i, a, b = 1, 1, 1
while i <= n:
if i <= 2:
res.append(1)
else:
res.append(sum(res[-2:])) #最後2位的總和
i += 1
return res # 傳回array
# 觀察記憶體佔用
fL = fibL(10000)
fY = fibY(10000)
fL2 = fibL(10)
fY2 = fibY(10)
print(type(fL), sys.getsizeof(fL)) # <class 'list'> 87624
print(type(fL2), sys.getsizeof(fL2)) # <class 'list'> 192
print(type(fY), sys.getsizeof(fY)) # <class 'generator'> 120
print(type(fY2), sys.getsizeof(fY2)) # <class 'generator'> 120
print(sys.getsizeof(list(fY))) # 83112 (將10000個費氏數列結果存入list中佔用之記憶體,可以看到和fL的大小很接近)
# list和generator兩者都是可迭代物件,所以都可以靠for來遍歷
for i in fL2:
print(i)
for i in fY2:
print(i)
print(len(fL2)) # 10
print(len(fY2)) # TypeError: object of type 'generator' has no len()
# 生成器沒有長度(因為他除非計算完成並將結果都儲存,要不然沒人知道他多長,除非你自己定義)
雖然兩者都可以迭代,但是傳回array的時候,作業系統一定得先分配一塊記憶體給結果然後再去迭代,所以如果你取越多個,佔用記憶體越大。
但生成器因為是惰性取值,到yield時才傳出計算結果,所以取10000個和10個佔用的記憶體是一樣的。
當然你會覺得2者行為很像,但是這內部表現不一樣你可以用的地方就不一樣了。
如果你的老闆今天跟你說我要取 1000000000000000000 個費氏數列並且產生檔案,用list的話你得跟他申請一台記憶體夠存 1000000000000000000 個的電腦,但是用yield你可以先把每次的計算結果存到硬碟就好。
yield版的理論上不管你n要多大,只要你電腦的記憶體能夠容納的下第n個的數列元素的大小就行。
list版的你得容納所有前n個元素總和。
你可以理解成yield版的是你跟電腦說:你把結果一個一個告訴我。
list版的是:你一次把結果告訴我。
所以你的理解沒錯,sql的結果很適合用yield來處理。
實際上sql本身也是用類似的惰性取值來處理。
所以cursor幾乎都有next()
感謝大大範例,感覺yield可以應用在很多地方,但是卻又很少人用,php來說,我猜是語言特性吧,記憶體不像某些語言吃得兇,而且大家還是習慣array的操作,相對來得簡單吧!所以較少人用吧,我也是少用好了,怕公司接收維護的人看不懂@@
php來說,我猜是語言特性吧,記憶體不像某些語言吃得兇
不是吧,只是生成器的觀念比較難理解,通常是在array這些基本資料結構之後才會介紹到。
而且實例上生成器的好處不一定能體現出來,除非你必須很節省記憶體,或是有像上面例子的這種必須緩存的需求,要不然沒一定要用生成器。
更不用說生成器你如果要得到所有結果你還是得把他存入array,在有些簡單程式裡不太直覺。
除非像是python這種積極把生成器應用在語法的語言,要不然會有這種不太被重視的結果很正常。
真的,生成器的概念,第一次看到的時候愣住了,這是什麼鬼,不用return就接好值還直接轉物件,像陣列 卻又不能用count取數,顛覆想像,哈哈哈。
感謝回覆!