dict.get(key)は機能するがdict [key]は機能しないのはなぜですか?


17

文字列にある1の数に基づいて、特定の数値のバイナリ文字列をグループ化しようとしています。

これは機能しません:

s = "0 1 3 7 8 9 11 15"
numbers = map(int, s.split())
binaries = [bin(x)[2:].rjust(4, '0') for x in numbers]

one_groups = dict.fromkeys(range(5), [])
for x in binaries:
    one_groups[x.count('1')] += [x]

予想される辞書one_groups

{0: ['0000'], 
 1: ['0001', '1000'], 
 2: ['0011', '1001'], 
 3: ['0111', '1011'], 
 4: ['1111']}

しかし、私は得る

{0: ['0000', '0001', '0011', '0111', '1000', '1001', '1011', '1111'], 
 1: ['0000', '0001', '0011', '0111', '1000', '1001', '1011', '1111'], 
 2: ['0000', '0001', '0011', '0111', '1000', '1001', '1011', '1111'], 
 3: ['0000', '0001', '0011', '0111', '1000', '1001', '1011', '1111'], 
 4: ['0000', '0001', '0011', '0111', '1000', '1001', '1011', '1111']}

これまでのところ機能しているのは、one_groups[x.count('1')] = one_groups.get(x.count('1')) + [x]代わりにone_groups[x.count('1')] += [x]

しかし、それはなぜですか?私が正しく思い出せば、dict[key]どのようにdict.get(key)機能するのと同じように、その辞書の値を返すはずではありませんか?私はこのスレッドを見ましたなぜdict [key]ではなくdict.get(key)ですか?しかし、この特定のケースに対する私の質問には答えませんでした。なぜなら、プログラムがKeyError

私も試しましたone_groups[x.count('1')].append(x)が、これもうまくいきません。


8
getNoneキーが存在しない場合、または指定されたデフォルト値の場合は戻り、キーが存在しない場合、インデックス演算子[]はエラーを発生させます。
adnanmuttaleb

サイドノートは、bin(x)[2:].rjust(4, '0')に簡略化できます'{:0>4b}'.format(x)
wjandrea

1
ところで、それは最小限の再現可能な例を作るのに役立ちます。この場合、どのように作成するかbinariesは質問には関係ないため、その値を提供するだけで済みます。
wjandrea

1
これはあなたの質問に答えますか?dict.fromkeysはすべて同じリストを指しています
Georgy

回答:


24

問題は可変性です。

one_groups = dict.fromkeys(range(5), [])-これは、値と同じリストをすべてのキーに渡します。したがって、1つの値を変更すると、すべてが変更されます。

それは基本的に言うことと同じです:

tmp = []
one_groups = dict.fromkeys(range(5), tmp)
del tmp

新しいリストを使用したい場合は、明示的なforループまたはdict内包のいずれかのループで行う必要があります。

one_groups = {key: [] for key in range(5)}

このことは、すべてのキーに対して「実行」する[](これはに等しいlist())ため、異なるリストで値を作成します。


なぜ機能するのgetですか?現在のリストを明示的に取得します+が、新しい結果リストを作成するためです。そして、それはそれはだかは問題ではありませんone_groups[x.count('1')] = one_groups.get(x.count('1')) + [x]one_groups[x.count('1')] = one_groups[x.count('1')] + [x]-重要なのはそこだということです+

私は誰もが言う方法を知っているa+=bだけであるa=a+bが、実装は、最適化のために異なる場合があります-リストの場合には、+=ちょうどある.extendので、新しいリストを作成すると、メモリの無駄になり、私たちは現在の変数で私たちの結果をしたい知っているので。


ああ、はい、理解しました。を使用して2Dリストを作成するときに、同様の問題があったことと、それを修正するmylist = [[] * 5] * 5方法も覚えてmylist = [[] for x in range(5)] * 5います。簡単に説明すると、理解したとおり、これは変数がその空のリストのメモリアドレスを指しているために発生します。これは、代わりにプリミティブを使用した場合に問題が発生しないことも意味しますか?
SpectraXCD

1
はい、プリミティブを使用している場合はこれで解決しone_groups[x.count('1')] += [x]ますが、リストをプリミティブ型に追加できないため機能しなくなり ます。より良い解決策は、代わりにdefaultdictを使用することです。
Fakher Mokadem

4
具体的には、を+呼び出し__add__て新しいオブジェクトを返す一方で、を+=呼び出し__iadd__、新しいオブジェクトを返す必要はありません
njzk2

8

問題は使用しています one_groups = dict.fromkeys(range(5), [])

(これにより、すべてのキーに同じリストが値として渡されます。したがって、1つの値を変更すると、すべてのキーが変更されます)


代わりにこれを使用できます: one_groups = {i:[] for i in range(5)}

(これにより、すべてのキーに対して[](list()と同じ)が "実行"され、異なるリストの値が作成されます。)


6
説明は本当に役に立ちますが、あなたは絶対に正しいです。2つの線の違いがはっきりしているわけではありません。
Simon Fink

はい、それは私の悪いです。申し訳ありません
Hameda169

4

これは、dictのfromkeysメソッドのヘルプです。

組み込み関数fromkeysのヘルプ:

builtins.typeインスタンスのfromkeys(iterable、value = None、/)メソッドイテラブルからのキーと値に値を設定して新しい辞書を作成します

つまり、fromkeysは値を受け入れ、呼び出し可能であっても、最初にそれを評価してから、その値をすべてのdictキーに割り当てます。

リストはPythonで変更可能であるため、同じ空のリスト参照が割り当てられ、1つの変更がすべてに影響します。

代わりにdefaultdictを使用してください:

>>> from collections import defaultdict
>>> one_groups = defaultdict(list)
>>> for x in binaries:
      one_groups[x.count('1')] += [x]
>>> one_groups = dict(one_groups) # to stop default dict behavior

これは、存在しないキーへの割り当てを受け入れ、値はデフォルトで空のリストになります(この場合)。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.