【お詫びして訂正】Pythonの粗大ゴミ

昨夜、Pythonの粗大ゴミというエントリを書いたが、このエントリを公開し、風呂に入ってさて寝ようとした時、ふと気になったことがあった。try-finally付きのジェネレータがgcで解放されないのなら、withブロックも一緒ではないだろうか?

あの記事は昔ジェネレータでtry-finallyが使えるようになった時(そう、最初はジェネレータ内でtry-finallyを使うとコンパイルエラーだったのだ)、ふと気になって調べた記憶を元に書いた。あの頃はたぶんまだwith文が無かった頃なので、withブロック中での動きは把握していない。

で、調べてみると、やっぱりwithブロック中のジェネレータオブジェクトが循環参照に含まれていると、try-finallyの場合と同様に解放されない事がわかった。次のようなジェネレータ

def uncollectable(arg):
    with context:
        yield 1
        yield 2
        yield 3

では、withコンテキストを抜ける時になんらかの処理が実行される可能性があるので、__del__()付きオブジェクトと同様に勝手に解放するわけにはいかないのだ。

また、try-finallyだけではなく、try-except中のジェネレータもgcでは解放されない。

def uncollectable(arg):
    try:
        yield 1
        yield 2
        yield 3
    except:
       print "Except"

Pythonの粗大ゴミをお読みになった皆さんと、Python Hack-a-thon 2011/2/19 で私のLTをお聞きになった皆さんには、お詫びして訂正する次第である。