2013-09-26 135 views
7

我寫了一個讀取txt文件的類。該文件由非空行(我們姑且稱之爲「段」),由空行分隔塊:蟒蛇發電機的發電機?

line1.1 
line1.2 
line1.3 

line2.1 
line2.2 

我的第一個執行是讀取整個文件並返回一個列表的列表,即是部分列表,其中每部分是行列表。 這在記憶方面顯然很糟糕。

所以我重新實現它作爲一個列表生成器,即在每個循環中,我的類以列表形式讀取內存中的整個部分並生成它。

這樣比較好,但在大型部分情況下仍存在問題。所以我想知道我是否可以將它重新實現爲發電機的發電機?問題是這個類是非常通用的,它應該能夠滿足這兩種使用情況:

  1. 讀取一個非常大的文件,包含非常大的部分,並且只循環一次。發電機的發電機是完美的。
  2. 將一個小文件讀入內存以多次循環。名單的發電機工作正常,因爲用戶可以直接調用

    列表(MyClass的(file_handle))

然而,發電機的發電不會的情況下,2工作,因爲內部的對象不會被轉換爲列表。

有什麼比實現一個明確的to_list()方法更優雅,它會將生成器生成器轉換爲列表列表?

+0

您是否嘗試過使用readline。通過這種方式只讀取單行的行;由新行分隔。這是在內存中加載小數據的好方法,除非你的線路本身很大。 – Vivek

+0

@Vivek我的線條非常複雜,我們每個線條都會生成一個驗證線條的對象,其狀態也取決於以前的線條。向用戶公開文件的內部格式不是一種選擇。 – crusaderky

+0

你可以給一個樣本輸入行... – Vivek

回答

6

的Python 2:

map(list, generator_of_generators) 

的Python 3:

list(map(list, generator_of_generators)) 

或兩者:

[list(gen) for gen in generator_of_generators] 

由於生成的對象是generator functions,不是單純的發電機,你'd想要做

[list(gen()) for gen in generator_of_generator_functions] 

如果這不起作用,我不知道你在問什麼。另外,它爲什麼會返回一個生成器函數而不是生成器本身?


自從你說你想避免list(generator_of_generator_functions)從神祕崩潰的評論,這取決於你真正想要的。

  • 這是可能覆蓋在這樣的list行爲:要麼你儲存子的發電機元件或不

  • 如果你真的得到一個崩潰,我建議耗盡每次主發生器迭代時都具有主發電機迴路的子發電機。這是標準做法,正是itertools.groupby所做的,stdlib生成器。

例如,

def metagen(): 
    def innergen(): 
     yield 1 
     yield 2 
     yield 3 

    for i in range(3): 
     r = innergen() 
     yield r 

     for _ in r: pass 
  • 或者使用的是深色的,祕密的破解方法,我將展示在莫」(我需要寫),但不這樣做!

如所承諾的,黑客(對於Python 3,這時候「輪):

from collections import UserList 
from functools import partial 


def objectitemcaller(key): 
    def inner(*args, **kwargs): 
     try: 
      return getattr(object, key)(*args, **kwargs) 
     except AttributeError: 
      return NotImplemented 
    return inner 


class Listable(UserList): 
    def __init__(self, iterator): 
     self.iterator = iterator 
     self.iterated = False 

    def __iter__(self): 
     return self 

    def __next__(self): 
     self.iterated = True 
     return next(self.iterator) 

    def _to_list_hack(self): 
     self.data = list(self) 
     del self.iterated 
     del self.iterator 
     self.__class__ = UserList 

for key in UserList.__dict__.keys() - Listable.__dict__.keys(): 
    if key not in ["__class__", "__dict__", "__module__", "__subclasshook__"]: 
     setattr(Listable, key, objectitemcaller(key)) 


def metagen(): 
    def innergen(): 
     yield 1 
     yield 2 
     yield 3 

    for i in range(3): 
     r = Listable(innergen()) 
     yield r 

     if not r.iterated: 
      r._to_list_hack() 

     else: 
      for item in r: pass 

for item in metagen(): 
    print(item) 
    print(list(item)) 
#>>> <Listable object at 0x7f46e4a4b850> 
#>>> [1, 2, 3] 
#>>> <Listable object at 0x7f46e4a4b950> 
#>>> [1, 2, 3] 
#>>> <Listable object at 0x7f46e4a4b990> 
#>>> [1, 2, 3] 

list(metagen()) 
#>>> [[1, 2, 3], [1, 2, 3], [1, 2, 3]] 

它是如此糟糕,我不想連解釋。

關鍵是你有一個包裝,可以檢測它是否被迭代,如果不是,你運行一個_to_list_hack,我沒有你,改變__class__屬性。

由於佈局有衝突,我們必須使用UserList類,併爲它的所有方法添加陰影,這只是另一層粗糙。

基本上,請不要使用這個黑客。不過,你可以盡情享受它。

0

一個比較務實的方式是在創建時告訴「發生器的生成器」是否生成生成器或列表。儘管這不像list神奇般地知道該怎麼做,但它似乎比具有特殊的功能更舒服。

def gengen(n, listmode=False): 
    for i in range(n): 
     def gen(): 
      for k in range(i+1): 
       yield k 
     yield list(gen()) if listmode else gen() 

根據listmode參數,這可以用於生成生成器或列表。

for gg in gengen(5, False): 
    print gg, list(gg) 
print list(gengen(5, True))