このコードが期待どおりに動作しない理由を理解するために、いくつかの助けが欲しいです。
辞書のキーを変更したいが、値を保持したい場合、彼/彼女は使用するかもしれません:
d[new_key] = d.pop[old_key]
すべてのキーを変更したい(そして値をそのままにしておきたい)が、以下のコードは特定の行をスキップする-( "col2")そのままです。辞書が順序付けられておらず、その中の値を変更し続けているためですか?
新しい辞書を作成せずにキーを変更して値を保持するにはどうすればよいですか?
import time
import pprint
name_dict = {"col1": 973, "col2": "1452 29th Street",
"col3": "Here is a value", "col4" : "Here is another value",
"col5" : "NULL", "col6": "Scottsdale",
"col7": "N/A", "col8" : "41.5946922",
"col9": "Building", "col10" : "Commercial"}
for k, v in name_dict.items():
print("This is the key: '%s' and this is the value '%s'\n" % (k, v) )
new_key = input("Please enter a new key: ")
name_dict[new_key] = name_dict.pop(k)
time.sleep(4)
pprint.pprint(name_dict)
繰り返し処理しているオブジェクトを変更することは決して良い考えではありません。通常、dict
は、あなたがそれを試みたときに例外を投げます:
name_dict = {1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6}
for k, v in name_dict.items():
name_dict.pop(k)
RuntimeError:反復中に辞書のサイズが変更されました
ただし、あなたの場合、削除されたアイテムごとに1つのアイテムを追加します。それにより、より複雑になります。何が起こっているのかを理解するには、辞書がまばらなテーブルに似ていることを知る必要があります。たとえば、{1: 1, 3: 3, 5: 5}
のような辞書は次のようになります(これはPython 3.6で変更され、3.6以降では以下は正しくありません):
hash key value
- - -
1 1 1
- - -
3 3 3
- - -
5 5 5
- - -
- - -
- - -
それは、それが繰り返される順序でもあります。したがって、最初の反復では、2番目の項目(1: 1
が格納されている)に移動します。キーを2
に変更し、キー1
を削除すると、dictは次のようになります。
hash key value
- - -
- - -
2 2 1
3 3 3
- - -
5 5 5
- - -
- - -
- - -
しかし、まだ2行目にあるので、次の反復では、次の「空でない」エントリ(2: 1
)に移動します。おっと...
文字列ハッシュは(セッションごとに)ランダム化されるため、キーとしての文字列ではさらに複雑になり、辞書内の順序は予測できなくなります。
3.6では、内部レイアウトが少し変更されましたが、同様のことがここで発生します。
このループがあると仮定します:
name_dict = {1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6}
for k, v in name_dict.items():
# print(k, k+6, name_dict.__sizeof__())
name_dict[k+6] = name_dict.pop(k)
# print(name_dict)
初期レイアウトは次のとおりです。
key value
1 1
2 2
3 3
4 4
5 5
6 1
最初のループは1
を削除しますが、7
を追加します。辞書は3.6で順序付けされているため、1
があったプレースホルダーが挿入されます。
key value
- -
2 2
3 3
4 4
5 5
6 1
7 2
これは、4
を10
に置き換えるまで続きます。
key value
- -
- -
- -
- -
5 5
6 1
7 2
8 3
9 4
10 5
ただし、5
を11
に置き換えると、辞書のサイズを大きくする必要があります。その後、何か特別なことが起こります:プレースホルダーが削除されます:
key value
6 6
7 1
8 2
9 3
10 4
11 5
したがって、最後の反復で位置5にいたので、行6を変更します。しかし、行6には現在11: 5
が含まれています。おっと...
代わりに、「変換テーブル」を保持し(それが「新しい辞書を作成せずに」要件に違反するかどうかはわかりませんが、コードを正常に動作させるために何らかのストレージが必要です)、ループ後に名前を変更します:
translate = {}
for k, v in name_dict.items():
print("This is the key: '%s' and this is the value '%s'\n" % (k, v) )
new_key = input("Please enter a new key: ")
translate[k] = new_key
time.sleep(4)
for old, new in translate.items():
name_dict[new] = name_dict.pop(old)
python3のdict.items()は、dictの単なるビューです。イテレート中にイテラブルを変更することは許可されていないため、dict.items()をイテレートする間にディクテーションを変更することは許可されていません。反復する前にitems()をリストにコピーする必要があります
for k, v in list(name_dict.items()):
...
name_dict[new_key] = name_dict.pop(k)
これは、リストに実際にすべてのデータの完全なコピーが保持されている場合でも、「新しい辞書なし」の要件を満たします。
キーだけをコピーすることで、メモリフットプリントを少し緩和できます。
for k in list(name_dict):
v = name_dict.pop(k)
...
name_dict[new_key] = v
編集:SvenKrügerの功績により、古いキーと新しいキーの衝突問題の可能性を提起しました。その場合、あなたは行く必要があります
kv = list(name_dict.items())
name_dict.clear()
for k, v in kv :
...
name_dict[new_key] = v
ところで、新しい辞書を作成しないユースケースがあります。現在の辞書は他の場所で参照されるかもしれません。
元の辞書に依存しない作業メモリ内の反復可能なオブジェクトを使用するには、fromkeys
メソッドを使用できます。これで、古いキーを新しいキーに割り当てることができます。ただし、留意しなければならないことが1つあります。特定の古いキーではない新しいキーに値を割り当てることはできませんが、新しいキーは古いキーセットの別のキーでもあります。
Old_Keys = { old_key_1, old_key_2, ..., old_key_n }
したがって、古いキーに関連する値を新しいキーに割り当てます。
old_key_1 -> new_key_1 not in Old_Keys # Okay!
old_key_2 -> new_key_2 == old_key_4 # Boom!... Error!...
以下を使用する場合は、このことに注意してください!
[〜#〜] code [〜#〜]
D = {'key1': 'val1', 'key2': 'val2', 'key3': 'val3'}
for key in D.fromkeys(D) :
new_key = raw_input("Old Key: %s, New Key: " % key)
D[new_key] = D.pop(key)
print D
[〜#〜] console [〜#〜]
Old Key: key1, New Key: abc
Old Key: key2, New Key: def
Old Key: key3, New Key: ghi
{"abc": 'val1', "def": 'val2', "ghi": 'val3'}