Python(通常のwhile True
以外))で無限ループを作成するさまざまな方法を試して、このアイデアを思い付きました:
x = {0: None}
for i in x:
del x[i]
x[i+1] = None # Value doesn't matter, so I set it to None
print(i)
紙の上では、これが無限にループする方法を追跡しました。
+ 1
は、辞書を更新する値None
を持つ新しいキーになります。これは、私の頭の中では、一種の無限ループ形式で自然数を出力するはずです:
0
1
2
3
4
5
.
.
.
このアイデアは賢いと思いましたが、Python 3.6で実行すると、次のように出力されます。
0
1
2
3
4
はい、5回繰り返した後、何とか停止しました。明らかに、ループのコードブロックには基本条件またはセンチネル値がないため、Pythonがこのコードを5回しか実行しないのはなぜですか?
ループで変更した場合、すべてのdictエントリを反復処理する保証はありません。 docs から:
辞書のエントリを追加または削除しながらビューを反復すると、RuntimeErrorが発生するか、すべてのエントリを反復できない可能性があります。
itertools.count()
を使用して、最初の試行と同様の「列挙型」無限ループを作成できます。例えば:
from itertools import count
for i in count():
print(i)
# don't run this without some mechanism to break the loop, i.e.
# if i == 10:
# break
# OUTPUT
# 0
# 1
# 2
# ...and so on
この場合、@ benvcが書いたように、これが機能することは保証されていません。しかし、なぜそれがC-Pythonで機能するのか疑問に思われる場合のために:
C-Python実装は、いくつかの挿入後にdictオブジェクトを破棄し、メモリ内の新しいスペースにコピーします。削除は気にしません。そのため、これが発生すると、ループがそれに気づき、例外で中断します。
これについての詳細、およびその他の興味深いpython内部については、こちらをご覧ください。
https://github.com/satwikkansal/wtfpython#-modifying-a-dictionary-while-iterating-over-it
私はあなたのコードをpython2とpython3でテストしました
python3 output
0,1,2,3,4
python2
0,1,2,3,4,5,6,7
起こっている可能性のあることが1つ思い浮かびます。最初のキー値を作成するときにディクショナリに割り当てられているメモリ量は一定であり、キー値を削除するときにメモリを割り当てたり割り当てを解除したりせず、値を削除するだけです。割り当てられたメモリがすべて使用されると、終了します。そのデルなしで実行すると、このエラーが発生するため
RuntimeError: dictionary changed size during iteration
したがって、pythonは、そのキーの値といくつかに十分なメモリを作成します。それが使用されると、ディクショナリに割り当てられたメモリはなくなります。
多くの人が指摘したように、for
ループを使用して反復中にデータ構造を変更することはお勧めできません。ただし、while
ループを使用すると、各反復でループ条件を再評価するため、これを行うことができます(まだ、代替案として提案されている人はいません)。正しいループ条件を見つける必要があります。スクリプトは次のようになる必要があります。
_x = {0: None}
while x:
i, _ = x.popitem()
print(i)
# to avoid infinite loop while testing
# if i == 10:
# break
x[i+1] = None
_
Pythonでは、ディクショナリは空のときは不正なものになります( docs を参照)。ループは、反復の開始時にx
が空の場合にのみ停止します。ディクショナリにはキーと値のペアが1つしかないため、そのペアを取得してディクショナリから削除するには popitem()
で十分です。辞書が空になった直後に次の整数が追加されるため、評価時にループ条件が偽になることはなく、無限ループになります。