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")