8

txt ファイルを読み取るクラスを作成しました。このファイルは、空行で区切られた空でない行のブロック (「セクション」と呼びましょう) で構成されています。

line1.1
line1.2
line1.3

line2.1
line2.2

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

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

これは改善されますが、セクションが大きい場合は依然として問題があります。それで、ジェネレーターのジェネレーターとして再実装できるのだろうか?問題は、このクラスが非常に一般的であり、これらのユース ケースの両方を満たすことができる必要があることです。

  1. 非常に大きなセクションを含む非常に大きなファイルを読み取り、一度だけ循環します。ジェネレーターのジェネレーターはこれに最適です。
  2. 小さなファイルをメモリに読み込み、複数回循環します。ユーザーは単に呼び出すことができるため、リストのジェネレーターは正常に機能します

    list(MyClass(ファイルハンドル))

ただし、内部オブジェクトがリストに変換されないため、ジェネレーターのジェネレーターはケース 2 では機能しません。

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

4

2 に答える 2

7

パイソン 2:

map(list, generator_of_generators)

パイソン 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.groupbystdlib ジェネレーターのジェネレーターです。

例えば。

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クラスを使用し、そのすべてのメソッドをシャドウする必要があります。

基本的に、このハックは使用しないでください。ユーモアとしても楽しめます。

于 2013-09-26T16:37:48.113 に答える