python の for ループ

ちょっくら面白い動きをしたので、晒してみる。
ループの中でコンテナを書き換えても、ループの終了判定やらには影響しないのかなーと思ったが、一歩間違えると無限ループ化も夢じゃないようだ。

>>> container = range(3)
>>> for i in container:
...     print 'i = %d' %i
...     print 'container = %s' %container
...
i = 0
container = [0, 1, 2]
i = 1
container = [0, 1, 2]
i = 2
container = [0, 1, 2]
>>>

意図通りの動きです。

>>> container = range(3)
>>> for i in container:
...     print 'i = %d' %i
...     container = 'container = %d' %i
...     print 'container = %s' %container
...
i = 0
container = container = 0
i = 1
container = container = 1
i = 2
container = container = 2
>>>

ループの中でコンテナを書き換えてみましたが、何事もなかったかのようにループが回りました。

>>> container = range(3)
>>> for i in container:
...     print 'i = %d' %i
...     container.append(i+3)
...     print 'container = %s' %container
...     if i == 10:
...             break
...
i = 0
container = [0, 1, 2, 3]
i = 1
container = [0, 1, 2, 3, 4]
i = 2
container = [0, 1, 2, 3, 4, 5]
i = 3
container = [0, 1, 2, 3, 4, 5, 6]
i = 4
container = [0, 1, 2, 3, 4, 5, 6, 7]
i = 5
container = [0, 1, 2, 3, 4, 5, 6, 7, 8]
i = 6
container = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
i = 7
container = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
i = 8
container = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
i = 9
container = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
i = 10
container = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
>>>

break しなかったら、無限ループに突入ですwww

2番目の例と、3番目の例が混在するのが面白いです。
、、、と思って、他の例をやってみました。

>>> container = range(3)
>>> for i in container:
...     print 'i = %d' %i
...     container[i] = 100
...     print 'container = %s' %container
...
i = 0
container = [100, 1, 2]
i = 1
container = [100, 100, 2]
i = 2
container = [100, 100, 100]
>>>

ちゃんと回りますねー

じゃー↓はどうだ!

>>> container = range(10)
>>> for i in container:
...     print 'i = %d' %i
...     container.remove(len(container)-1)
...     print 'container = %s' %container
...
i = 0
container = [0, 1, 2, 3, 4, 5, 6, 7, 8]
i = 1
container = [0, 1, 2, 3, 4, 5, 6, 7]
i = 2
container = [0, 1, 2, 3, 4, 5, 6]
i = 3
container = [0, 1, 2, 3, 4, 5]
i = 4
container = [0, 1, 2, 3, 4]
>>>

ここまでを簡単にまとめると、、、

  • ループの終了判定は都度コンテナの内容を参照してやっている。
  • コンテナを参照する際のポインタは、カウンタとは独立した別の”何か”らしい。
  • 参照されるコンテナの内容が変更されていても、ループ用の(内部的な)ポインタの指し先が関係なければ、普通にループは回るらしい。

で、も、ね。

>>> container = range(10)
>>> for i in container:
...     print 'i = %d' %i
...     container = range(3)
...     print 'container = %s' %container
...
i = 0
container = [0, 1, 2]
i = 1
container = [0, 1, 2]
i = 2
container = [0, 1, 2]
i = 3
container = [0, 1, 2]
i = 4
container = [0, 1, 2]
i = 5
container = [0, 1, 2]
i = 6
container = [0, 1, 2]
i = 7
container = [0, 1, 2]
i = 8
container = [0, 1, 2]
i = 9
container = [0, 1, 2]
>>>

これが動くということは、ループで使用しているcontainerと内部で使用しているcontainerが別インスタンスという認識でOKかなぁ。

つまり、、、

  • 下の例だと、ループに使っているcontainer が変更される。
  • >>> for i in container:
    ...     container.append(i+3)
    
  • で、下の例だと、別インスタンスがcontainer に入る。
  • >>> for i in container:
    ...     container = range(3)
    
    • この場合、元のcontainer のインスタンスが残っている(ただしcontainer のポインタ先は新しい range(3) のインスタンスなので、問題なく動く。
    • 、、、が、ガベージコレクションのタイミングによっては、危ない。
    • (でも、実はforループが参照しているから、元のcontainerインスタンスはガベージコレクション対象ではないようにも見える。)

こんなところでどうでしょう。(間違いがあったら、コメントをお願いしますね。)
コードとして、どういう場面で有効に使えるか、、、というのは、甚だ疑問だが、面白い。

まぁ、そもそもこんな危険が危ないwコードは書かないようにしませう。

コメントをどうぞ

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

*


次のHTML タグと属性が使えます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <img localsrc="" alt="">