Comment initialiser un dictionnaire de listes vides en Python?

88

Ma tentative de créer par programme un dictionnaire de listes ne me permet pas d'adresser individuellement les clés de dictionnaire. Chaque fois que je crée le dictionnaire de listes et que j'essaie d'ajouter une clé, elles sont toutes mises à jour. Voici un cas de test très simple:

data = {}
data = data.fromkeys(range(2),[])
data[1].append('hello')
print data

Résultat actuel: {0: ['hello'], 1: ['hello']}

Résultat attendu: {0: [], 1: ['hello']}

Voici ce qui fonctionne

data = {0:[],1:[]}
data[1].append('hello')
print data

Résultat réel et attendu: {0: [], 1: ['hello']}

Pourquoi la fromkeysméthode ne fonctionne-t-elle pas comme prévu?

Martin Burch
la source

Réponses:

111

Passer []comme deuxième argument à dict.fromkeys()donne un résultat plutôt inutile - toutes les valeurs du dictionnaire seront le même objet de liste.

Dans Python 2.7 ou supérieur, vous pouvez utiliser à la place une compréhension de dictionnaire:

data = {k: [] for k in range(2)}

Dans les versions antérieures de Python, vous pouvez utiliser

data = dict((k, []) for k in range(2))
Sven Marnach
la source
3
Eh bien, c'est un comportement plutôt peu intuitif, une idée sur pourquoi le même objet est utilisé pour toutes les clés?
Bar
2
@Bar Parce qu'il n'y a rien d'autre que la fonction puisse faire dans la sémantique du langage Python. Vous transmettez un seul objet à utiliser comme valeur pour toutes les clés, de sorte qu'un seul objet est utilisé pour toutes les clés. Il serait préférable que la fromkeys()méthode accepte une fonction de fabrique à la place, afin que nous puissions passer en listtant que fonction, et cette fonction serait appelée une fois pour chaque clé créée, mais ce n'est pas l'API réelle de dict.fromkeys().
Sven Marnach
3
Ce n'est pas du tout intuitif. Cela m'a pris une heure à trouver. Merci
Astrid
1
La même chose se produit si vous passez dict () comme deuxième argument. Comportement très déroutant.
Orly
@Orly C'est parce qu'un premier dictionnaire vide est créé, puis une référence à celui-ci est transmise à toutes les initialisations.
Dr_Zaszuś
83

Utilisez à la place defaultdict :

from collections import defaultdict
data = defaultdict(list)
data[1].append('hello')

De cette façon, vous n'avez pas à initialiser au préalable toutes les touches que vous souhaitez utiliser pour les listes.

Ce qui se passe dans votre exemple, c'est que vous utilisez une liste (mutable):

alist = [1]
data = dict.fromkeys(range(2), alist)
alist.append(2)
print data

produirait {0: [1, 2], 1: [1, 2]}.

Martijn Pieters
la source
2
Dans mon cas, je dois initialiser toutes les clés au préalable pour que le reste de la logique du programme puisse fonctionner comme prévu, mais ce serait une bonne solution sinon. Merci.
Martin Burch
Je suppose que ce qui manque à cette réponse est de dire que cette solution fonctionne, par opposition à celle de l'OP, car listici ce n'est pas une liste vide, mais un type (ou vous pouvez le voir comme un constructeur appelable, je suppose). Ainsi, chaque fois qu'une clé manquante est transmise, une nouvelle liste est créée au lieu de réutiliser la même.
Dr_Zaszuś
8

Vous remplissez vos dictionnaires avec des références à une seule liste. Ainsi, lorsque vous la mettez à jour, la mise à jour est reflétée sur toutes les références. Essayez plutôt une compréhension du dictionnaire. Voir Créer un dictionnaire avec compréhension de liste en Python

d = {k : v for k in blah blah blah}
cobie
la source
excellente suggestion sur l'initialisation des valeurs du dictionnaire ... merci cobie! J'ai étendu votre exemple pour réinitialiser les valeurs dans un dictionnaire existant, d. J'ai effectué ceci comme suit: d = {k: 0 for k in d}
John
Que vcontient cette réponse?
Dr_Zaszuś
-2

Vous pouvez utiliser ceci:

data[:1] = ['hello']
Conner Dassen
la source
2
Il pourrait être utile au PO d'expliquer pourquoi cela fonctionne. La question d'origine publiée demande pourquoi cela ne fonctionne pas comme prévu.
william.taylor.09
@ william.taylor.09 il est assez évident pourquoi cela fonctionne, n'est-ce pas?
Conner Dassen
OP demande (était) "Pourquoi la méthode fromkeys ne fonctionne-t-elle pas comme prévu?"
william.taylor.09