ホームページ >バックエンド開発 >Python チュートリアル >Python での辞書の走査中に要素を変更すると例外が発生する

Python での辞書の走査中に要素を変更すると例外が発生する

高洛峰
高洛峰オリジナル
2017-03-02 16:49:241127ブラウズ

まず、Python で辞書を走査する基本的な方法をいくつか確認してみましょう:

スクリプト:

#!/usr/bin/python 
dict={"a":"apple","b":"banana","o":"orange"} 
 
print "##########dict######################" 
for i in dict: 
    print "dict[%s]=" % i,dict[i] 
 
print "###########items#####################" 
for (k,v) in dict.items(): 
    print "dict[%s]=" % k,v 
 
print "###########iteritems#################" 
for k,v in dict.iteritems(): 
    print "dict[%s]=" % k,v 
 
print "###########iterkeys,itervalues#######" 
for k,v in zip(dict.iterkeys(),dict.itervalues()): 
    print "dict[%s]=" % k,v

実行結果:

##########dict###################### 
dict[a]= apple 
dict[b]= banana 
dict[o]= orange 
###########items##################### 
dict[a]= apple 
dict[b]= banana 
dict[o]= orange 
###########iteritems################# 
dict[a]= apple 
dict[b]= banana 
dict[o]= orange 
###########iterkeys,itervalues####### 
dict[a]= apple 
dict[b]= banana 
dict[o]= orange

さて、それでは「ビジネス」に移ります。

Python 辞書の走査に関する「議論」....
最初に抜粋:

#这里初始化一个dict
>>> d = {'a':1, 'b':0, 'c':1, 'd':0}
#本意是遍历dict,发现元素的值是0的话,就删掉
>>> for k in d:
...  if d[k] == 0:
...   del(d[k])
...
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
RuntimeError: dictionary changed size during iteration
#结果抛出异常了,两个0的元素,也只删掉一个。
>>> d
{&#39;a&#39;: 1, &#39;c&#39;: 1, &#39;d&#39;: 0}

>>> d = {&#39;a&#39;:1, &#39;b&#39;:0, &#39;c&#39;:1, &#39;d&#39;:0}
#d.keys() 是一个下标的数组
>>> d.keys()
[&#39;a&#39;, &#39;c&#39;, &#39;b&#39;, &#39;d&#39;]
#这样遍历,就没问题了,因为其实其实这里遍历的是d.keys()这个list常量。
>>> for k in d.keys():
...  if d[k] == 0:
...   del(d[k])
...
>>> d
{&#39;a&#39;: 1, &#39;c&#39;: 1}
#结果也是对的
>>>

#这里初始化一个dict
>>> d = {&#39;a&#39;:1, &#39;b&#39;:0, &#39;c&#39;:1, &#39;d&#39;:0}
#本意是遍历dict,发现元素的值是0的话,就删掉
>>> for k in d:
...  if d[k] == 0:
...   del(d[k])
...
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
RuntimeError: dictionary changed size during iteration
#结果抛出异常了,两个0的元素,也只删掉一个。
>>> d
{&#39;a&#39;: 1, &#39;c&#39;: 1, &#39;d&#39;: 0}
 
>>> d = {&#39;a&#39;:1, &#39;b&#39;:0, &#39;c&#39;:1, &#39;d&#39;:0}
#d.keys() 是一个下标的数组
>>> d.keys()
[&#39;a&#39;, &#39;c&#39;, &#39;b&#39;, &#39;d&#39;]
#这样遍历,就没问题了,因为其实其实这里遍历的是d.keys()这个list常量。
>>> for k in d.keys():
...  if d[k] == 0:
...   del(d[k])
...
>>> d
{&#39;a&#39;: 1, &#39;c&#39;: 1}
#结果也是对的
>>>

実際、この問題はもともと非常に単純です。つまり、辞書が走査されるが、途中で変更される場合です。要素の追加や削除などのトラバーサルを実行すると、トラバーサルが終了し、反復中にディクショナリ サイズが変更されたという例外がスローされます。解決策は、ディクショナリ キー値をトラバースし、そのディクショナリ キー値に基づいてトラバースすることです。値はトラバーサルの継続には影響しません。
しかし、ここに別の優れた専門家が高い意見を述べています:

まず第一に、Python は adict の for k の形式の反復子の使用を推奨しています。次に、C++ STL や Python などのライブラリでは、トラバーサル中にコンテナ内の要素を削除することはお勧めできません。この状況は設計計画に問題があることを示していることが多く、すべてに python に対応する特別な要件があるためです。 adict.key() を使用してコピーを作成します。最後に、すべての Python コンテナはスレッドの安全性を保証するものではありません。これを複数のスレッドで実行したい場合は、それをロックする必要があります。これは、ビジネス コードの設計に問題があることを示しています。

この特殊なケースは、「dict をトラバースするときに、d.keys() で for k を使用する習慣を身につける」という結論につながります。これを修正する必要があると思います。通常のトラバースでは、 adict の for k を使用する必要があります。

さらに、「トラバーサル中に要素を削除する」という要件については、Python のアプローチは adict = {k, v for adict.iteritems() if v != 0} または alist = [i for i in alist if i != 0 ]

この書き方を見て私の目は輝きました:なぜこの文法がまだ残っているのですか?

よく見ると、彼は次のようなことを言っているのかもしれません:

#!/usr/bin/env python
# -*- coding=utf-8 -*-
a = {&#39;a&#39;:1, &#39;b&#39;:0, &#39;c&#39;:1, &#39;d&#39;:0}
b={}
for k,v in a.items():
  if v != 0:
    b.update({k:v})
adict = b
del b
print a

#!/usr/bin/env python
# -*- coding=utf-8 -*-
a = {&#39;a&#39;:1, &#39;b&#39;:0, &#39;c&#39;:1, &#39;d&#39;:0}
b={}
for k,v in a.items():
  if v != 0:
    b.update({k:v})
adict = b
del b
print a

それが正しいかどうかはわかりません。

この書き方をすると最初は三項演算子を思い出したので、詳しく見てみると、そうではないことがわかりました

val = float(raw_input("Age: "))
status = ("working","retired")[val>65]
print "You should be",status

val = float(raw_input("Age: "))
status = ("working","retired")[val>65]
print "You should be",status

val>65 は論理式です。 、0または1を返すのでちょうどいいです。前のタプルのIDとして使用できるのは非常に優れています。 。 。

しかし、Googleの情報には別のバージョンがあります

#V1 if X else V2
s = None
a = "not null" if s == None else s
print a
#&#39;not null&#39;

その後、Huanangユーザーグループ(中国のPython技術メーリングリスト)で言及され、多くの専門家が次のように答えました:


>>> alist = [1,2,0,3,0,4,5]
>>> alist = [i for i in alist if i != 0]
>>> alist

[1, 2, 3, 4, 5]

>>> d = {&#39;a&#39;:1, &#39;b&#39;:0, &#39;c&#39;:1, &#39;d&#39;:0}
>>> d = dict([(k,v) for k,v in d.iteritems() if v!=0])
>>> d
{&#39;a&#39;:1,&#39;c&#39;:1&#39;}

If Python>=2.7 より大きい

次の記述方法も使用できます:

>>> d = {k:v for k,v in d.iteritems() if v !=0 }


Python での辞書走査中の要素の変更によって引き起こされる例外に関するその他の関連記事については、PHP 中国語 Web サイトに注目してください。


声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。