2

この再帰関数(ジェネレーター)があるとしましょう

data = [1,[21, 22, [231, 232, 233]], [31, 32, 33]]

def listgen(data):
    for each in data:
        if type(each) is int:
            yield str(each)
        elif type(each) is list:
            g = listgen(each)
            for i in g:
                yield i
        else:
            continue
gen = listgen(data)
print ' '.join(gen)

出力は次のとおりです。

1 21 22 231 232 233 31 32 33

さて...そのジェネレーターに.close()メソッドを定義する必要があるので、どうすればそれをクラスとして書くことができますか?

4

1 に答える 1

2

closeジェネレーターを呼び出すとGeneratorExit、実行が現在一時停止されている時点でaが発生します。その例外をキャッチし、例外をexcept再発生させる前にブロックでクリーンアップを実行できます(無視することはできません)。

再帰スタックに例外を手動で伝播する必要があるため、再帰ジェネレーターの場合は少し複雑になります。しかし、それでもそれほど難しくはありません。これがジェネレーターのバージョンで、閉じたときに実行の各レベルで「クリーンアップ」を出力します。サンプルデータセットから231が得られた直後に閉じると、リスト内のネストの各レイヤーに1つずつ、合計3回取得されます。実際のユースケースでは、これをコードに置き換えてデータベース接続を閉じるか、その他のクリーンアップ作業を行うことができます。

def closable_listgen(data):
    try:
        for each in data:
            if type(each) is int:
                yield str(each)
            elif type(each) is list:
                g = closable_listgen(each)
                try:
                    for i in g:
                        yield i
                except GeneratorExit:
                    g.close()
                    raise
            else:
                continue
    except GeneratorExit:
        print("cleaning up")
        raise

Python 3.3では、新しいyield from構文が呼び出しをcloseジェネレーターのチェーンの上位に自動的に伝播するため、これが少し簡単になります。

def closable_listgen33(data):
    try:
        for each in data:
            if type(each) is int:
                yield str(each)
            elif type(each) is list:
                g = closable_listgen33(each)
                yield from g
            else:
                continue
    except GeneratorExit:
        print("cleaning up")
        raise

適切な方法として、ジェネレータをクラスとして実装する方法を次に示します(これは最初に要求したものです)。

class list_gen_class(object):
    def __init__(self, data):
        self.iterator = iter(data)
        self.child = None

    def __iter__(self):
        return self

    def __next__(self):
        while True:
            if self.child:
                try:
                    return next(self.child)
                except StopIteration:
                    self.child = None

            value = next(self.iterator)
            if isinstance(value, int):
                return str(value)
            elif isinstance(value, list):
                self.child = list_gen_class(value)

    def close(self):
        if self.child:
            self.child.close()

        print("cleaning up")
于 2012-11-22T12:35:38.343 に答える