Pythonのfor文のリストの評価は一度だけ
pythonのfor文を使う時に
for i in xrange(len(list)):
って書き方をすると思うのですが、
len()が毎回実行されたりするのかなぁ?と思って気になりました。
何か関数とかをここで実行した時にパフォーマンスに関わるのかな?という疑問ですね。
確認するためにはfor文の中でlist要素を削除してみて、イテレーションがおかしくなるかどうかを見れば良さそう。
元の要素数分反復していればOKということで以下のように実行。
>>> list = ['a', 'b', 'c', 'd', 'e', 'f'] >>> for i in xrange(len(list)): # 6回ループしてくれればOK。 ... print i ... if i / 2: ... del(list[0]) ... 0 1 2 3 4 5 >>> list ['e', 'f']
要素が削除され、それとは関係なく6回実行されたので問題ないのですね。
念のためリファレンス確認してみたら書いてありました。
式リストは一度だけ評価されます;
そうなんですね。そうなんですね。よかった。
と、思って最後まで読んだところ、
どの要素が次に使われるかを追跡するために、内部的なカウンタが使われており、このカウンタは反復処理を行うごとに加算されます。 このカウンタがシーケンスの長さに達すると、ループは終了します。このことは、スイート中でシーケンスから現在の (または以前の) 要素を 除去すると、(次の要素のインデクスは、すでに取り扱った要素のインデクスになるために) 次の要素が飛ばされることを意味します。
という警告がありました。
確認してみると、
>>> list = ['a', 'b', 'c', 'd', 'e', 'f'] >>> for x in list: ... print x ... if True: ... del list[0] ... a c e >>> list ['b', 'd', 'f']
おぉ。確かに。
b, d, fは要素が減ったタイミングでindexがズレてスキップされたのね。
さらに続き読んでみると、ちゃんと対策も書いてありました。
シーケンス全体に相当するスライスを使って一時的なコピーを作ると、これを避けることができます。
for x in a[:]: if x < 0: a.remove(x)
こういうところでリストのコピーを使ったりするのですね。