2013-09-26 7 views
7

私はtxtファイルを読み込むクラスを作成しました。このファイルは、(のは、「セクション」と呼んでみましょう)非空行のブロックで構成空行で区切られます。発電機のパイソン発電機?

line1.1 
line1.2 
line1.3 

line2.1 
line2.2 

私の最初の実装では、ファイル全体を読み込み、リストのリストを返す、ということでしたセクションのリストです。各セクションは行のリストです。 これは明らかにメモリが賢明でした。

私はそれをリストのジェネレータとして再実装しました。つまり、サイクルごとにクラスがメモリ内のセクション全体をリストとして読み込み、それを生成します。

これは優れていますが、大きなセクションの場合はまだ問題があります。だから私はジェネレータのジェネレータとして再実装できるのだろうか?問題は、このクラスは非常に一般的であり、これらの使用例の両方を満足させることができるはずです。

  1. 一度だけそれを介して非常に大きなセクション、サイクルを含む、非常に大きなファイルを読み込みます。発電機の発電機はこれに最適です。
  2. メモリに小さなファイルを読み込んで複数回サイクリングします。ユーザーはちょうど

    リスト(MyClassの(file_handle))、内側のオブジェクトがない場合と同じように

しかし、発電機の発電機は、ケース2には機能しないを呼び出すことができるので、リストの発電機は、正常に動作しますリストに変換する。

ジェネレータのジェネレータをリストのリストに変換する明示的なto_list()メソッドを実装するよりも洗練された方法はありますか?

+0

readlineでの作業を試したことがありますか?このようにして、1行の行だけが読み込まれます。新しいラインで区切られています。これは、ライン自体が巨大でない限り、小さなデータをメモリに読み込むための良い方法です。 – 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ではありませんので、あなたはやりたいと思ったのです

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

これが機能しない場合、私はあなたが何を求めているのか分かりません。また、ジェネレータ関数ではなく、ジェネレータ自体を返すのはなぜですか?あなたは不思議にクラッシュからlist(generator_of_generator_functions)を避けたかったと述べたコメントの中で、これはあなたが本当に欲しいものに依存しているので


  • このようにlistの振る舞いを上書きすることはできませんです:どちらかサブ発電要素またはあなたが本当にクラッシュを取得した場合は、私は排気お勧めしませを

  • 保存しますメインジェネレータが繰り返し実行されるたびにメインジェネレータループを持つサブジェネレータ。これは標準的な練習であり、まさにitertools.groupbyが何をするのですか、stdlib generator-of-generatorsです。

例えば、

  • または私はMO」(私はそれを書く必要がある)で紹介しますが、それをしない、暗い、秘密のハックの方法を使用し
    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クラスを使用し、すべてのメソッドをシャドーする必要があります。これは、crudの別のレイヤーです。

基本的には、このハッキングを使用しないでください。あなたはユーモアとしてそれを楽しむことができます。

0

実際には、ジェネレータやリストを生成するかどうかを作成時に「ジェネレータのジェネレータ」に伝えるのが実際的な方法です。 listは魔法のように何をすべきかを知っていると便利ではありませんが、それでも特別なto_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)) 
関連する問題