Python踩坑日記——耿直open欲吞,怎奈MemoryError不肯

我叫“Snake”,在我佛如來(Guido van Rossum)的指引下,我開始了一段取經之路。猴哥的稱號是“鬥戰聖佛”,我的稱號是“Python”。一條蛇的取經故事,我稱之爲《蛇經》。

 

                                              

在我的修行道路上,會有各種各樣的怪。因爲他們的形式不同,所以我一直在學習新的打怪技能,爲的就是能控制他們,爲我所用。我佛慈悲,先招將,不從再殺。

 

一天,我收到處理一些數據的傳令,並被告知數據在某個文件裏面。我是沒能力上天入地的,有些辦不到的事情,我就會求救於“天將”或者“土地”。喊土地(內置函數)很簡單,他們就在我身邊,每次需要他們幫忙的時候,我就直接呼喚他們。叫天將(標準庫)就不一樣了,不是隨隨便便就能讓他們來幫你幹活的。取經前,上天給了我塊“請神令”——import,後面有機會我們再談吧。

 

先說這次的問題,文件裏的東西我是沒辦法直接拿到的。在需要從文件裏拿數據的情況下,我都會找一位土地——open()。他很厲害,這是我們第一次見面時,他的自我介紹:

 

你好,很高興您能召喚我,自我介紹下,我能去不毛之地——文件裏把數據帶回來。您可以指定我的四項功能。

第一:文件路徑。告訴我去哪裏,之後您就別管了,我會代步的。

第二:打開方式。告訴您要做什麼,是往文件裏送人(寫數據),還是從文件裏往外帶人(讀數據)。參數有‘r’(read讀),‘w’(write),‘b’(binary以二進制形式打開文件),‘+’(r and w),‘a’(add,在文件末尾追加東西)等等。這些個參數您可以同時指定給我,比如 ‘rb’,我就會知道您是想讓我以二進制形式讀數據,其他的相同。

第三:編碼方式。告訴我,文件的編碼方式,我會按照那種方式來處理數據,如果您讓我處理的方式和數據不匹配,那我處理完後的數據您可能不認得(亂碼)。

第四:緩衝區個數。這個功能一般不需要您操作,但是我還是要說下。您知道我會把數據帶到指定文件裏,但是文件最終都會被存放到一個叫“磁盤”的地方。也就是說,我會一直往“磁盤”裏帶數據。如果您將我的這項參數設爲1,那我每遇到一個換行符,我就會去磁盤走一次。如果您讓我帶的數據有10行,那我就要去10次。

如果您設置的是大於1的某個值n,每當夠n個字節,我就會走一次。

如果是0的話,一有數據我就會帶走。

如果是負值或者不指定,我就會默認系統值。所以這項功能關乎着我的勞動量,請慎重。

 

很簡明又有趣的介紹。這裏讓我突然想到我們最開始合作時出現的那個很嚴重的問題。

 

open每次打開文件後,自己不會回來,而是派一個小信使(文件對象)回來完成任務,自己待在原處使文件一直處於打開狀態。

 

                                                            

 

oh!你好,我是open的信使,需要我做什麼?隨時效力~

 

很可愛的小信使,我給他隨便起了個名字叫f(也就是file的意思),然後問他:“都能做什麼?”

 

他說:“可讀可寫,什麼都沒問題。”

 

有趣,“你讀數據的功能有什麼特殊之處嗎”

 

“我讀數據的方法有很多,你可以直接讓我read。我會把文件裏的所有內容處理成一個字符串(一種數據的類型,這種屬性的怪有很多處理方法,你先理解成一種怪的類別。)給你,read(10)的話,我會讀10個字節給你。如果你想讓我只讀一行,我可以readline。如果......”

 

“等會~,先給我一行數據看看。”

 

“好嘞,稍等~”,小信使拿出他的信封開始查看,不久給了我一行數據。我很是欣喜,之後我們聊了很多,聊着聊着我突然想到,我忘了給小信使剛剛給我的字符串起名字(變量賦值)。比如開始的時候我給小信使起了名字叫f,所有我才能用他做f.readline()這件事。字符串名字沒有起,我就找不到他了,他如果四處跑,最後會被“檢察官”認定爲無證子民,從這個世界處理掉。(垃圾回收機制,不再使用的數據進行清理)

 

我一拍腦門,對小信使說,“麻煩再去readline下吧,那個字符串我忘記起名字了,給弄丟了。”我一臉鬱悶。

 

“沒問題,稍等。”不一會後,我就又得到了一個字符串,我剛要感謝,突然發現問題不對。

 

“等等!這個不是剛纔的字符串啊,和剛剛的不一樣?”

 

“當然不一樣啦~ 這是第二行的數據。”

 

“你怎麼讀了第二行的數據呢?”

 

“聽我給你講哈,每次我讀完數據,我就會在最後做個標記。下次再讀的時候,我會從做的標記開始。開始的時候我讀了一行數據,我就會做個標記。後來我又讀了一行數據,就從那個標記開始讀一行,所以就讀了第二行的數據。”

 

       

 

“哦~ 懂了,也就是說你會記住你上次的操作是嗎?如果我讓你再執行readline,你就會給我第三的數據?”

 

“對的,就是這樣~”

 

“那下次我再讓open來幫忙,你還會記得你讀到哪裏了嗎?”

 

“哦,不會了,當你完成這個任務後,我就會消失。你下次再找open老大幫忙的時候,他會再派一個信使,那已經不是我了。他會重新開始的。”

 

一想到下次就見不到他了,突然有點悲傷。

 

他看出了我的顧慮,道:“別擔心,雖然不是我了。但那個哥們還是信使,能和我做相同的事情。”

 

“嗯嗯,現在我懂了,也就是說,我每次請open打開文件,你們信使(文件對象)的標記(指針)是從0開始的,也就是文件開頭。但是我現在不想重新打開文件,我想讓你在幫我讀下第一行的數據,還有辦法做到嗎?”

 

“當然啦~ 你可以修改我的標記啊~”

 

“修改標記?”

 

“對,因爲我每次都會從標記處開始讀取,你就可以修改我的標記,下次讀取的時候,我就會從那裏開始的。”

 

“也就是說我現在把你的標記改成 ‘0’ ,你下次讀的時候就從頭開始了是嗎?”

 

“對的,給你筆~”。

 

我拿着這支叫seek的筆。然後執行了一段命令:f.seek(0)

 

“ok,修改成功了,我現在的標記在0處,也就是文件開頭了。”

 

“那個,使用這支筆的時候,我還有一些其他需要注意的東西嗎?”

 

“您還真是好學啊,有的。其實seek的參數有兩個,一個就是您剛剛輸入的offset,我們把它叫做‘偏移量’,單位是‘字節’。還有一個參數我叫它whence,如果您不指定,我就默認爲是0了。這個參數表示從哪個位置開始偏移。0表示從文件開頭開始偏移,1表示從當前標記開始算起,2表示從文件末尾開始算起。舉個例子,比如您剛剛執行的命令是seek(0),其實完整地代碼命令是——seek(0, 0),也就是從文件開始位置偏移0個字節的地方做標記,所以我的標記就做到開頭了。如果您輸入的命令是seek(0, 1),本質上我的標記是不用動的,因爲您指定的標記是從當前位置開始,偏移0個字節,也就是沒有改變。”

 

“那whence爲2的時候呢?它已經在文件末尾了,後面沒有數據,標記怎麼做呢?”

 

“如果當whence爲2,並且offset  >=  0的時候,我就會把標記做到文件末尾。如果這個時候您還讓我讀數據,我就只能給您返回空字符串了。”

 

“offset >= 0? 你是說offset還能是負數?”

 

“哇塞,您還注意到這一點啦~,是的。我可以處理負數,正數既然是往後偏移,負數自然就是往左偏移了~,比如說offset是-1,我就會把標記做到whence指定位置的前一個字節處,如果是-2,我就會做當前兩個字節處的~”

 

“哇~ 小信使真厲害”。我心裏默默地想。

 

“不過這裏是有個問題的,您還記得您派open老大去文件的時候有個打開方式嗎?”

 

“我記得,我記得我設置成了‘r’,也就是read的意思。”

 

“對,就是這裏。如果您在用seek筆的時候,whence指定了1或者2,offset只能是0。如果不是0的話,就會出現可怕的後果,那件事情“天機處”(後面會談到)告訴我叫做‘io.UnsupportedOperation’——IO不支持操作,我是這麼叫的。”

 

“那我怎樣才能正常使用seek筆呢?”

 

“哦,您記得下次讓open老大打開文件的方式是二進制讀取就可以了,也就是把那個參數設成'rb'。‘b’就是binary的意思。”

 

“ok, 瞭解了~ 幫大忙了。”

 

“等下,還有件事情要告訴您~  我還有個工具叫tell。”

 

“哦~,幹什麼用的?”

 

他邪魅的一笑,“告訴您我當前的標記在什麼位置。”

 

“想的還挺周到的~”,我笑着說。

 

 

“你還有什麼能力向我展示嗎?”

 

“很多呢,比如讀數據的時候,我除了可以readline還可以readlines。如果讓我執行readlines,我會把每行數據當成一個元素,放到list(列表,我取經路上四大保鏢之一,每次我處理數據,都會放到保鏢那裏,讓他們幫我做事情。比如把數據們排排序啊,把所有數據都加一啊,之類的。後面有機會,我們再來說說我的保鏢們吧~)裏面。”

 

“嗯,這個很好用,你把每行的數據給了list,那之後我就可以從list裏面拿數據了。也就不用一直麻煩你了。”

 

“還有呢~,這些功能只是讀數據,我還可以往文件裏寫數據,只需要執行write,當然了,你派open老大過去的時候,要告訴他文件的打開方式是‘w’(write寫)才行。”

 

“還有沒有更炫酷的功能!!!”期待臉。

 

“我還可以writelines()。你可以給我一個“序列”(我的四大保鏢都是序列部門的),我可以把序列的內容一條條寫進文件裏去。”

 

“斯蓋~~,還有嗎?”

 

“我還有個next()功能,和readline()一樣......”

 

bla, bla, bla, bla, bla, bla, bla, bla, bla, bla, bla, bla, bla, bla, bla, bla, bla, bla, bla, bla......

 

 

 

 

那次我們聊了很久,讓我差點忘記了正事:

 

我迅速地寫下兩條命令,拿到了第一行的數據(這次沒有忘記起名字。汗~~~~):

f = open('test.txt', 'r')
f.readline()
#以上爲歷史信息
f.seek(0)
data = f.readline()

其實我只需要文件的第一行數據,當我拿到他後,就去忙別的了。沒有再去讓小信使做別的事,我是後來才知道的,原來他一直在等我。因爲我不告訴他任務結束,他是不會走的!

 

我是怎麼知道的呢。說起來,還是我犯的錯誤。那次我讓open幫忙存儲數據......。

 

那天我用了半天的時間處理完一堆數據,看着他們工工整整的樣子,我很是欣慰。於是我想把他們存儲起來,以備後面我再次用到他們,也省的我下次再次處理了。

 

我迅速的寫下下面指令:

f = open('test.txt', 'w')

f.write('abc')

 

我往test.txt文件裏寫入了一個字符串‘abc’。

 

幾天後,我想把test.txt文件裏的數據拿出來。於是我又找來土地open幫忙,我告訴他:“麻煩去“test.txt”,‘r’方式打開。”

 

不久後,他派的小信使回來了,“請指示~ sir”

 

“麻煩幫我讀出第一行數據。”

 

“收到,readline執行中,請稍等~。讀取完畢,請接收......”,他遞給我一個字符串。

 

“謝謝,......”我的話還未說完,就發現了問題,小信使給我的竟然是個空字符串。“你怎麼不好好工作呢?我讓你讀取數據,你怎麼給我個空串!”

 

“可是文件就是空的呀......”信使有些委屈。

 

“啥?! 空的! 不可能,上次我明明派另一個小信使write了的。你把你們open老大喊回來,我要和他聊聊!”

 

“如果open老的回來,我就會消失的。”

 

“嗯,你的工作已經做完了,去喊你們老大吧。”

 

“工作完了?您是想讓我現在執行close()命令嗎?”

 

“close()命令? 啥意思?”我一臉疑惑。

 

“啊?沒人和您說過嗎。如果您需要信使做的事情都做完了,您要告訴他,任務結束,可以關閉文件了。不然open老大會一直在那邊打開着文件,我也一直會存在的。”

 

“呀呀呀~,這麼回事啊。如果我讓你write了,不管你,一直等到程序結束,這個世界不再存在了,會怎樣。”

 

“那文件裏的東西會先被清空,只有當我執行close之後,我才能把數據寫進去,open老大也才能把他們保存到“磁盤”裏。不然直到程序結束,我都沒有close的話,文件就成空的了。”

 

“呀呀呀,這麼回事啊,錯怪你了,是我的問題,抱歉......”

 

“沒關係,現在是否需要我執行close命令呢?”

 

“嗯,需要執行,不過在那之前,你先幫我把‘abc’寫到文件裏面去吧。”

 

“Got it~”

 

f.write('abc')

f.close()

 

不久後,open回來了。我手頭也沒有什麼事,和他閒聊的時候說到了這件事。

 

“抱歉啊,每次打開文件,我都沒有關,讓你一直守在文件那裏,直到程序結束。”

 

“沒事沒事,信使不給我報信,我一直以爲你還有工作要做,所以我也就一直處於打開狀態。”

 

“這是個問題啊,這次的數據比較小,丟了沒關係。萬一後面有些核心數據,我write完後忘了close可怎麼辦~”,我一陣擔憂。

 

“的確是個問題......哎!”open大叫。

 

“怎麼了,一驚一乍的。”

 

“觀音不是給了你很多鱗片嗎,讓你看情況使用,我們可以約定個規則啊。”

 

話說,猴哥取經的時候,觀音給了他三根救命毫毛,以備燃眉之需。給我的,是三十三片鱗片(關鍵字,又叫保留字)。和猴哥的不同,猴哥的毫毛用一次少一根,而我的可以重複使用。

 

“啊~,我都給忘了。”

 

“給你的鱗片都有什麼,我們可以拿來用幾片。”open問。

 

“呀,這個怎麼說呢,我記不住所有的鱗片名字呀。太多了,常用的還行。”

 

“這是個問題......”open愁眉不展。

 

“噯~,差點把這事給忘了。我還有請神令呢~”

 

“請神令?”

 

“嗯,因爲你們土地在地下,我們直接喊來幫忙。但是如果喊天將來幫忙,就需要“請神令”了,這是開始如來給我的。”

 

“你要請誰過來呢?”

 

“keyword!”

 

“哦~這位天將是作甚的呢。”

 

“他記錄着我所有的鱗片名字,觀音特別封的職。”

 

“吼~ 快讓我開開眼。”

 

於是我用import請神令,呼喊keyword星君。

 

import keyword

 

片刻時間,keyword星君站到了我的面前。

 

“哦,你好,需要我做什麼?”

 

“那個,鱗片的名字我給忘記了,您幫我看看唄。”

 

“嗨,多大點事啊,稍等啊~”

 

星君執行了他的kwlist功能,然後把數據給了我的保鏢list暫存起來。

 

“open土地也在啊,你看到print土地了嗎?”星君問。

 

“沒有啊,我們不經常在一起合作。你需要他來幫你把數據輸出出來嗎?”open迴應到。

 

“是啊,沒有他把數據展示出來,你們看不到啊。”星君解釋。

 

“我可以找他來。”我一邊說着,趕緊讓print土地出來幫忙了。

 

星君看到print土地開心了,“看來我們又要合作了,小仙忘記了觀音給的鱗片名稱,你來幫我輸出一下,讓小仙看看吧。”

 

“沒問題。”print土地迴應。

 

於是二人合作起來。加上list保鏢的幫襯~

 

import keyword

print(keyword.kwlist)

 

刷刷刷,33片鱗片全擺在了我的面前。

 

我大喜,對keyword和print說:“謝謝了,幫大忙了。”

 

“不客氣,需要我們幫忙,隨叫隨到~”,二人同語。

 

留下open與我二人面對着33枚鱗片:

我們討論了好久,選擇了with和as這兩枚鱗片加以利用。

 

“這樣吧,你以後用with來標記我,用as給小信使起名字。之後你用一個 ‘:’ 另起一行,你需要小信使做的所有任務都進行縮進,我和小信使約好,等把你縮進裏面的任務都做完,就去執行close指令。我們就默認你做完了,你也就不用操心close了,我們自己來。”open一邊說着,一邊給我畫了張架構圖:

 

with open('test.txt', 'r') as f:

      data = f.readline()

      data2 = f.readline()

print(data)

print(data2)

 

“好比這樣,當小信使執行完你的data和data2的賦值命令後,他就close回來向我報告。我也就不用一直在文件那裏等着你了。”open土地說到。

 

“真是個好辦法,這樣我就不用一直擔心着close了~,謝謝你啦。”

 

那天我們把酒言歡,好不痛快。

 

 

宏圖建好後,我命令下達方式的轉變:

               

 

 

 

這之後的一段時間裏,我和open配合的天衣無縫,我們一起做了很多工作。直到發生了那件事,現在開始,我才說到了重點。

 

那天天氣不錯,一切正常。我像往常一樣喊open來幫忙,我給了open文件位置,告訴他‘r’(read)。土地領命後,告辭前往。不久後,派回來的小信使站到了我的面前。

 

“麻煩把數據全讀出來。”我說。

 

“要執行read()嗎?”信使確認性的問。

 

“嗯,read。”

 

“正在執行,稍等~”

 

過了一段不短的時間,小信使還在讀數據,看來這次的數據量有點大啊。我耐心的等着~

 

不久後,我發現情況有些不對勁,天空變了顏色,黑紅色的雲,悶聲亂叫的閃電。周圍的環境變得烏煙瘴氣的,靜靜感受,貌似連大地都有些顫抖起來。

 

“怎麼回事!”我大喊。正當我驚歎的時候,整個世界崩潰掉了(程序拋出異常,中斷執行)。所有正在幹活的人都被清理掉了。

 

其實,每次我執行任務的時候,如來都會創造個世界,讓我在這個世界工作。完成任務後,我們所有的人退出,如來再把創造的世界收回。下次我有任務的時候,如來還會做相同的事情。

 

可是這次的不同,因爲世界的消失(程序的退出)不是如來做的(程序正常運行結束),而是不知哪裏出了問題。果然沒過多久,我被人接到了“天庭”,來探討此次事故的原因。

 

我和如來老兒踱步來到“天機處”(控制檯),這裏記錄了整個世界的運行狀況。出現了問題,“天機處”也會有記錄。

 

我們來到“天機處”的工作間,大屏幕上赫然的閃爍着幾個大字:  MemoryError

 

“啥意思?”我疑惑的問。

 

“我也不太清楚,不過應該不是天庭的問題。走吧,去看看《天地輪迴錄》,那裏面記載了我們整個世界的運行規律。可不止我創造的世界哦~”

 

那天我沒回去,和如來老兒一起讀遍了《天地輪迴錄》,貌似找到了答案。

 

我們的世界是在一個叫“內存”的傢伙肚子裏的。平常我們不用的時候,會被放到一個叫“磁盤”的傢伙肚子裏。需要我們的時候,就把我們帶到內存裏來,只有在這裏,我們才能工作。但是“內存”和“磁盤”相比,雖然速度快,但是小很多。

 

那,問題就找到答案了。我需要用的數據需要從磁盤帶進內存裏面, 數據量超過內存的大小後,內存就頂不住了。所以我們的世界拋出了異常,被迫中止。

 

“得啦,原因也找到了,剩下的工作就是你的了。遇到大數據的時候,不要一次把他們帶進來,先帶進來一部分。處理完後,再帶下一部分,別又‘天崩地裂’了。”如來囑咐道。

 

“行,我回去考慮下怎麼辦。”

 

“噯,對了,我最近新封了個官職叫this,你可以用請神令叫他來哦~”

 

“哦?幹什麼的”我好奇的問。

 

“呵呵,你看到自會明白。”

 

剛出了天庭,我就等不急了,立馬使用了請神令。

 

import this

 

長見識,this天兵帶給我一段話:

Beautiful is better than ugly.

Explicit is better than implicit.

Simple is better than complex.

Complex is better than complicated.

Flat is better than nested.

Sparse is better than dense.

Readability counts.

Special cases aren't special enough to break the rules.

Although practicality beats purity.

Errors should never pass silently.

Unless explicitly silenced.

In the face of ambiguity, refuse the temptation to guess.

There should be one-- and preferably only one --obvious way to do it.

Although that way may not be obvious at first unless you're Dutch.

Now is better than never.

Although never is often better than *right* now.

If the implementation is hard to explain, it's a bad idea.

If the implementation is easy to explain, it may be a good idea.

Namespaces are one honking great idea -- let's do more of those!

 

“吼,有意思,以後下命令就向這個目標努力了”我心裏想。一邊想着,我腳下也沒有停,趕緊回去睡覺。第二天還要和open探討怎樣讀大數據的問題呢。

 

 

“我們的世界一下裝不下那麼多的數據,如來讓我們每次不要讀進來那麼多,處理完一部分再讀下一部分。”我沒說過多廢話,直奔主題。

 

“原來是這麼回事......”open摸着他的花白鬍須,細細思索。“哎,你前段時間不是用def鱗片搞了個叫‘封裝’的東西嗎。我們能不能用用它。”

 

的確,因爲我在工作過程中有許多相似的命令,比如我對一個字符串可能做去掉第一個字節,然後去掉末尾字節,最後將字符串裏面的‘a’全部去除這麼一套操作。雖然有很多字符串,但是操作都是一樣的,爲了我不用一直髮命令,我就把這一套命令封裝了起來,我又用了觀音給的一個鱗片def。封裝的這段命令我叫做“函數”。

 

def doSomething(string):

      blabla

      blabla

      blabla

 

我每次對一個字符串處理的時候,直接交給def封裝的doSomething這個函數。像這樣:

doSomething(‘abc’),我就不用再把命令說一遍了。

 

“你的消息還挺靈通,但是怎麼利用函數呢?”我問。

 

“記得前一次,我看到你的鱗片裏有個yield,我們用下他吧。”open應道。

 

“怎麼用?”

 

“這樣,你的函數一般在最後不是有返回值嗎。我們規定在函數裏如果有yield,當程序走到yield的時候,就返回yield後面的內容,並且讓函數記住執行到哪了。下次再用這個函數的時候,從標記的位置繼續運行。”

 

“嗯?這和解決讀大數據問題有什麼關係?”

 

“你腦子怎麼回事,我們可以在函數裏面寫個循環啊。每次就yield一部分數據出來,等我們處理完,再調用那個函數。他就會從上次的位置再yield一部分數據,這不就解決啦?”

 

“哦!對啊,好辦法呢。”

 

“這樣吧,我們把這種使用的時候才生成數據的結構叫generator生成器,顧名思義,就是數據生成的地方。”

 

“好!”

 

於是,對於大數據讀取問題,我常常會這麼處理:

def read_file(fpath): 
                     BLOCK_SIZE = 1024 
                     with open(fpath, 'rb') as f: 
                             while True: 
                                       block = f.read(BLOCK_SIZE) 
                                       if block:
                                            yield block 
                                       else:
                                             pass

 

我每次先調用這個函數,得到他的一個信使,也就是一個generator對象。然後讓信使給我1024個字節。我處理完之後,再從他那裏拿一次,一點點的,我就處理完畢了。(哦,對了,這裏我又用了一枚鱗片pass,我把他叫做佔位符,望文生義,就是佔位的,什麼也不做。while也是一枚鱗片,他的功能是循環,只要while後面的條件滿足,就會一直做他縮進裏面的命令。if和else也是,他們起判斷的作用。)

 

後來我們給yield正了名,說明了他的地位:

yield的作用就是把一個函數變成一個generator,在調用函數的時候,函數不會執行,而是返回一個generator對象。這裏要區分一個概念,拿我上面的read_file函數來說,read_file是一個generator function,而read_file(‘test.txt’)是調用read_file返回的一個generator。

 

生成器有個__next__()功能方便我的使用,每次我用他的時候,直接調用生成器對象的__next__()方法來得到他的下段數據。當生成器把所有的數據都給我後,如果我還調用__next__()方法,就會拋出StopIteration異常。

 

                                        

 

總結來說,爲什麼生成器能解決這個問題呢?是因爲生成器在你需要數據的時候纔開始計算生成,而不是一開始就把所有的數據都產出來,放在內存裏,等着你來用。

 

爲此,上面還特意授予了個官職,來判斷一個函數是不是生成器函數。

 

from inspect import isgeneratorfunction

 

就是他,isgeneratorfunction(),人和他的名字一樣,那麼的直白。是就是,不是就不是。

 

不久,我在我們的世界宣佈了generator的產生,說明了他的功能。就在我剛剛宣佈完畢這件事情後,open的小信使就發話了:

“我本身就有這個功能啊!但是我們叫做Iterator,你們說的generator是Iterator的一種,只是更方便使用罷了。”

 

我看向身邊的open,想要得到迴應。他卻向我攤攤手,看來他都不知道他的信使(返回對象)是Iterator。

 

“也就是說,我可以直接循環遍歷你本身嘍?”

 

“沒問題啊。”信使顯得很開心

 

“走,去試試。”

 

我先創建了個文件,然後寫了點內容:

 

                                                           

 

做起了實驗:

                                                     

這裏我用到了我的四大保鏢之一list,就是在這裏叫content的那位,如果後面有機會,我們再來談談他的故事,這裏只要知道他的append功能是把後面的數據存到他這裏就行了。我們先來說open的小信使吧。

 

結果你猜怎麼着?還真行:

                                                           

小信使還告訴我,collections宮裏有位Iterator星君,他就是一個Iterator。你可以讓isinstance土地來幫忙,看看我是不是個Iterator。

 

我趕緊把人家請來,確認了一番。

 

                                                

 

人家isinstance土地告訴我了:

                                                                       

 

我從天上找到地下,沒想到最開始的小信使就可以解決這個問題。真是——衆裏尋芳千百度,驀然回首,那人卻在燈火闌珊處。

 

最後我們總結說下,讀取大數據問題的根本方法是,分批次讀入。而實現的技術是generator(或者是Iterator),open()返回對象也可以使用,不過每次只返回一行。而用yield生成的generator,我們可以自定義每次讀入的數據量。

 

取經路上,我和土地open的故事在這裏就告一段落了。沒準以後我們還會出現更多的問題,誰知道呢~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章