2011-10-13 129 views
0

讀取文件並通過分隔符突出行的最佳方式是什麼? 返回的數據應該是元組列表。Python - 讀取文件並通過分隔符突出行的最佳方式

這種方法可以被毆打嗎?這可以做得更快/使用更少的內存?

def readfile(filepath, delim): 
    with open(filepath, 'r') as f: 
     return [tuple(line.split(delim)) for line in f] 
+0

是的,但不是那樣的。 –

回答

14

您發佈的代碼讀取整個文件,並在內存中構建一份文件副本,作爲所有文件內容的單個列表,分割爲元組,每行一個元組。既然你問到有關如何使用較少的內存,你可能只需要一個發生器功能:

def readfile(filepath, delim): 
    with open(filepath, 'r') as f: 
     for line in f: 
      yield tuple(line.split(delim)) 

BUT!有一個重要的警告!你只能迭代readfile返回的元組一次。

lines_as_tuples = readfile(mydata,','): 

for linedata in lines_as_tuples: 
    # do something 

到目前爲止,這是可以的,生成器和列表看起來一樣。但假設您的文件將包含大量浮點數,並且您通過文件的迭代計算出這些數字的總體平均值。您可以使用「#做某事」代碼來計算總數和數字的數量,然後計算平均值。但是現在讓我們假設你想再次迭代,這次要找出每個值的平均值之間的差異。你可能會認爲你只是增加一個for循環:

for linedata in lines_as_tuples: 
    # do another thing 
    # BUT - this loop never does anything because lines_as_tuples has been consumed! 

BAM!這是生成器和列表之間的巨大差異。現在代碼中的這一點,生成器已經被完全消耗掉 - 但沒有提出特殊的異常,for循環根本沒有做任何事情,並繼續,默默地!

在許多情況下,您將返回的列表只會迭代一次,在這種情況下,將readfile轉換爲生成器將會很好。但是,如果你想要的是一個更持久的列表,你將訪問多次,那麼使用一個生成器會給你帶來問題,因爲你只能迭代一次生成器。

我的建議?使readline成爲一個生成器,因此,對於世界來說,它只是產生文件的每個增量位,非常好,並且具有內存效率。將數據保留的負擔放到調用者上 - 如果調用者需要多次引用返回的數據,那麼調用者可以簡單地從生成者構建自己的列表 - 使用list(readfile('file.dat', ','))可以很容易地在Python中完成。

+0

精彩地解釋保羅! –

+0

我想你總是可以創建另一個生成器。 'newlines_as_tuples = readfile(mydata,',')' –

+0

當然,假設創建生成器比保留列表更便宜 - 這可能是從文件讀取的情況,但如果生成器從數據庫中產生行,或來自網絡服務器的數據,我不太確定。 – PaulMcG

3

內存使用可以通過使用發電機,而不是一個列表,列表,而不是一個元組降低,所以你不需要讀取整個文件到內存中一次:

def readfile(path, delim): 
    return (ln.split(delim) for ln in open(f, 'r')) 

儘管如此,您將不得不依靠垃圾收集器來關閉文件。至於返回元組:不要這樣做,如果沒有必要,因爲列表速度要快一小部分,構造元組只需要一分鐘的成本,並且(重要的)你的行將被分割成可變大小的序列,這些序列在概念上是列表的。

我猜,速度只能通過下降到C/Cython級別來改善; str.split很難被打敗,因爲它是用C編寫的,列表解析是AFAIK Python中最快的循環結構。

更重要的是,這是非常明確的Pythonic代碼。我不會嘗試優化這一點,除了發電機位。

+0

你有沒有試過用這種方式在'file'上下文管理器中使用genex? –

+0

我喜歡它@larmans它不會生成元組列表,但我想你可以添加元組函數。返回(tuple(line.split(delim))用於f中的行) –

+0

@MattAlcock:爲什麼不構建元組添加了一點。 –

相關問題