生成器 用於優化大檔案的讀取等

2021-08-28 11:08:38 字數 3201 閱讀 5542

如果是做python或者其他語言的小夥伴,對於生成器應該不陌生。但很多php開發者或許都不知道生成器這個功能,可能是因為生成器是php 5.5.0才引入的功能,也可以是生成器作用不是很明顯。但是,生成器功能的確非常有用。

直接講概念估計你聽完還是一頭霧水,所以我們先來說說優點,也許能勾起你的興趣。那麼生成器有哪些優點,如下:

那麼,這些神奇的功能究竟是如何做到的?我們先來舉個例子。

首先,放下生成器概念的包袱,來看乙個簡單的php函式:

function createrange($number)

return $data;

}

這是乙個非常常見的php函式,我們在處理一些陣列的時候經常會使用。這裡的**也非常簡單:

我們建立乙個函式。

函式內包含乙個 for 迴圈,我們迴圈的把當前時間放到$data裡面

for迴圈執行完畢,把 $data 返回出去。

下面沒完,我們繼續。我們再寫乙個函式,把這個函式的返回值迴圈列印出來:

$result = createrange(10); // 這裡呼叫上面我們建立的函式

foreach($result as $value)

我們在瀏覽器裡面看一下執行結果:

這裡非常完美,沒有任何問題。(當然 sleep(1) 效果你們看不出來)

我們注意到,在呼叫函式 createrange 的時候給 $number 的傳值是10,乙個很小的數字。假設,現在傳遞乙個值10000000(1000萬)。

那麼,在函式 createrange 裡面,for迴圈就需要執行1000萬次。且有1000萬個值被放到 $data 裡面,而$data陣列在是被放在記憶體內。所以,在呼叫函式時候會占用大量記憶體。

這裡,生成器就可以大顯身手了。

我們直接修改**,你們注意觀察:

function createrange($number)

}

看下這段和剛剛很像的**,我們刪除了陣列 $data ,而且也沒有返回任何內容,而是在 time() 之前使用了乙個關鍵字yield。我們再執行一下第二段**:

$result = createrange(10); // 這裡呼叫上面我們建立的函式

我們奇蹟般的發現了,輸出的值和第一次沒有使用生成器的不一樣。這裡的值(時間戳)中間間隔了1秒。

這裡的間隔一秒其實就是 sleep(1) 造成的後果。但是為什麼第一次沒有間隔?那是因為:

到這裡,你應該對生成器有點兒頭緒。

下面我們來對於剛剛的**進行剖析。

function createrange($number)

}$result = createrange(10); // 這裡呼叫上面我們建立的函式

foreach($result as $value)

我們來還原一下**執行過程。

首先呼叫 createrange 函式,傳入引數10,但是 for 值執行了一次然後停止了,並且告訴 foreach 第一次迴圈可以用的值。

foreach 開始對 $result 迴圈,進來首先 sleep(1) ,然後開始使用 for 給的乙個值執行輸出。

foreach 準備第二次迴圈,開始第二次迴圈之前,它向 for 迴圈又請求了一次。

for 迴圈於是又執行了一次,將生成的時間戳告訴 foreach .

foreach 拿到第二個值,並且輸出。由於 foreach 中 sleep(1) ,所以, for 迴圈延遲了1秒生成當前時間

所以,整個**執行中,始終只有乙個記錄值參與迴圈,記憶體中也只有一條資訊。

無論開始傳入的 $number 有多大,由於並不會立即生成所有結果集,所以記憶體始終是一條迴圈的值。

到這裡,你應該已經大概理解什麼是生成器了。下面我們來說下生成器原理。

首先明確乙個概念:生成器yield關鍵字不是返回值,他的專業術語叫產出值,只是生成乙個值

那麼**中 foreach 迴圈的是什麼?其實是php在使用生成器的時候,會返回乙個 generator 類的物件。 foreach 可以對該物件進行迭代,每一次迭代,php會通過 generator 例項計算出下一次需要迭代的值。這樣 foreach 就知道下一次需要迭代的值了。

而且,在執行中 for 迴圈執行後,會立即停止。等待 foreach 下次迴圈時候再次和  for  索要下次的值的時候,迴圈才會再執行一次,然後立即再次停止。直到不滿足條件不執行結束。

很多php開發者不了解生成器,其實主要是不了解應用領域。那麼,生成器在實際開發中有哪些應用?

php開發很多時候都要讀取大檔案,比如csv檔案、text檔案,或者一些日誌檔案。這些檔案如果很大,比如5個g。這時,直接一次性把所有的內容讀取到記憶體中計算不太現實。

這裡生成器就可以派上用場啦。簡單看個例子:讀取text檔案

我們建立乙個text文字文件,並在其中輸入幾行文字,示範讀取。

通過上圖的輸出結果我們可以看出**完全正常。

但是,背後的**執行規則卻一點兒也不一樣。使用生成器讀取檔案,第一次讀取了第一行,第二次讀取了第二行,以此類推,每次被載入到記憶體中的文字只有一行,大大的減小了記憶體的使用。

這樣,即使讀取上g的文字也不用擔心,完全可以像讀取很小檔案一樣編寫**。

迭代器和生成器,使用生成器讀取大檔案

可迭代型別 和 迭代器 什麼是迭代協議。迭代器是什麼?是訪問集合類的一種方式,一般用來遍歷資料。for迴圈可以遍歷資料 迭代器和以下標的訪問方式不一樣,迭代器是不能返回的 後退 alist 0 alist 2 alist 1 是不行的 下標訪問 背後的原理 協議 是 getitem 迭代器提供了一種...

python使用生成器的原理遍歷大檔案

需求遍歷大小為500g的檔案 檔案特殊之處只有一行,一行就有500g,每條資料使用分隔符 隔開 def readline f,separator 讀取資料的方法 param separator 分隔符 return none bar while true while separator in bar...

python 生成器讀取檔案遇到的問題

attributeerror generator object has no attribute rewind 這裡記錄下,看看有什麼解決辦法沒 2020.8.21 都忘了這檔子事了 現在把之前臨時用的方法記錄下 1.重新讀取乙份一模一樣的資料 2.不用那個rewind函式 初始化計算資訊 ifno...