Je voudrais de l'aide pour comprendre pourquoi ce code ne fonctionne pas comme prévu.
Si on veut changer la clé d'un dictionnaire mais garder la valeur, il/elle peut utiliser:
d[new_key] = d.pop[old_key]
Je veux modifier toutes les clés (et garder les valeurs en place) mais le code ci-dessous ignore certaines lignes - ("col2") reste inchangé. Est-ce parce que les dictionnaires ne sont pas ordonnés et que je continue à en changer les valeurs?
Comment pourrais-je changer les clés et conserver les valeurs sans créer un nouveau dictionnaire?
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)
Ce n'est jamais une bonne idée de changer l'objet sur lequel vous faites une itération. Normalement, dict
lève même une exception lorsque vous l'essayez:
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: la taille du dictionnaire a changé pendant l'itération
Cependant, dans votre cas, vous ajoutez un élément pour chaque élément supprimé. Cela le rend plus convolutionné. Pour comprendre ce qui se passe, vous devez savoir qu'un dictionnaire est un peu comme une table clairsemée. Par exemple, un dictionnaire tel que {1: 1, 3: 3, 5: 5}
pourrait ressembler à ceci (cela a changé dans Python 3.6, pour les versions 3.6 et plus récentes, ce qui suit n'est plus correct):
hash key value
- - -
1 1 1
- - -
3 3 3
- - -
5 5 5
- - -
- - -
- - -
C'est aussi l'ordre dans lequel il est itéré. Ainsi, lors de la première itération, il ira au deuxième élément (où le 1: 1
est stocké). Supposons que vous modifiez la clé en 2
et supprimiez la clé 1
, le dict aurait ressemblé à ceci:
hash key value
- - -
- - -
2 2 1
3 3 3
- - -
5 5 5
- - -
- - -
- - -
Mais nous sommes toujours à la deuxième ligne, donc la prochaine itération passera à la prochaine entrée "non vide" qui est 2: 1
. Oups ...
C'est encore plus compliqué avec des chaînes en tant que clés car les hachages de chaînes sont aléatoires (par session), ainsi l'ordre dans le dictionnaire est imprévisible.
En 3.6, la disposition interne a été légèrement modifiée, mais quelque chose de similaire se produit ici.
En supposant que vous ayez cette boucle:
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)
La disposition initiale est comme ceci:
key value
1 1
2 2
3 3
4 4
5 5
6 1
La première boucle supprime 1
mais ajoute 7
. Comme les dictionnaires sont ordonnés en 3.6, cela insère un espace réservé où 1
était:
key value
- -
2 2
3 3
4 4
5 5
6 1
7 2
Ceci continue jusqu'à ce que vous remplaciez 4
par 10
.
key value
- -
- -
- -
- -
5 5
6 1
7 2
8 3
9 4
10 5
Mais lorsque vous remplacez 5
par 11
, le dictionnaire devra augmenter sa taille. Puis quelque chose de spécial se produit: les espaces réservés sont supprimés:
key value
6 6
7 1
8 2
9 3
10 4
11 5
Donc, nous étions à la position 5 de la dernière itération et maintenant nous changeons de ligne 6. Mais la ligne 6 contient 11: 5
pour le moment. Oups ...
Vous pourriez plutôt conserver une "table de traduction" (vous ne savez pas si cela ne respecte pas votre obligation "sans créer de nouveau dict", mais vous avez besoin d'une sorte de stockage pour que votre code fonctionne correctement) et renommer après la boucle:
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)
en python3, dict.items () n'est qu'un aperçu du dict. comme vous n'êtes pas autorisé à modifier une variable itérative lors d'une itération, vous n'êtes pas autorisé à modifier un dict lors d'une itération sur dict.items () . vous devez copier des éléments () dans une liste avant une itération
for k, v in list(name_dict.items()):
...
name_dict[new_key] = name_dict.pop(k)
cela ne répond pas à votre exigence "pas de nouveau dict", même si la liste contient en fait une copie complète de toutes vos données.
vous pouvez relâcher un peu l'empreinte mémoire en copiant uniquement les clés
for k in list(name_dict):
v = name_dict.pop(k)
...
name_dict[new_key] = v
EDIT: grâce à Sven Krüger, il a évoqué la possibilité d’un problème de collision vieille-clé-nouvelle clé. dans ce cas, vous devez aller chercher
kv = list(name_dict.items())
name_dict.clear()
for k, v in kv :
...
name_dict[new_key] = v
à propos, il y a un cas d'utilisation pour ne pas créer un nouveau dict, celui qui est en cours pourrait être référencé ailleurs.
Pour avoir un objet itérable dans votre mémoire de travail qui ne dépend pas de votre dictionnaire d'origine, vous pouvez utiliser la méthode fromkeys
. Il est maintenant possible d'assigner de nouvelles clés avec vos anciennes valeurs. Mais il y a une chose que vous devez garder à l'esprit: vous ne pouvez pas attribuer une valeur à une nouvelle clé qui ne soit pas l'ancienne clé, alors que la nouvelle clé est également une autre clé de l'ancien jeu de clés.
Old_Keys = { old_key_1, old_key_2, ..., old_key_n }
Vous affectez donc la valeur liée à l'ancienne clé à la nouvelle clé.
old_key_1 -> new_key_1 not in Old_Keys # Okay!
old_key_2 -> new_key_2 == old_key_4 # Boom!... Error!...
Soyez conscient de cela lorsque vous utilisez ce qui suit!
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'}