Python créant un dictionnaire de listes

214

Je veux créer un dictionnaire dont les valeurs sont des listes. Par exemple:

{
  1: ['1'],
  2: ['1','2'],
  3: ['2']
}

Si je fais:

d = dict()
a = ['1', '2']
for i in a:
    for j in range(int(i), int(i) + 2): 
        d[j].append(i)

J'obtiens une KeyError, car d [...] n'est pas une liste. Dans ce cas, je peux ajouter le code suivant après l'attribution d'un pour initialiser le dictionnaire.

for x in range(1, 4):
    d[x] = list()

Y a-t-il une meilleure manière de faire cela? Disons que je ne connais pas les clés dont je vais avoir besoin jusqu'à ce que je sois dans la deuxième forboucle. Par exemple:

class relation:
    scope_list = list()
...
d = dict()
for relation in relation_list:
    for scope_item in relation.scope_list:
        d[scope_item].append(relation)

Une alternative serait alors de remplacer

d[scope_item].append(relation)

avec

if d.has_key(scope_item):
    d[scope_item].append(relation)
else:
    d[scope_item] = [relation,]

Quelle est la meilleure façon de gérer cela? Idéalement, l'ajout «fonctionnerait tout simplement». Existe-t-il un moyen d'exprimer que je veux un dictionnaire de listes vides, même si je ne connais pas toutes les clés lorsque je crée la liste pour la première fois?

user118662
la source

Réponses:

279

Vous pouvez utiliser defaultdict :

>>> from collections import defaultdict
>>> d = defaultdict(list)
>>> a = ['1', '2']
>>> for i in a:
...   for j in range(int(i), int(i) + 2):
...     d[j].append(i)
...
>>> d
defaultdict(<type 'list'>, {1: ['1'], 2: ['1', '2'], 3: ['2']})
>>> d.items()
[(1, ['1']), (2, ['1', '2']), (3, ['2'])]
viande_mecanique
la source
1
D'autres dictionnaires sous le collectionsmodule fonctionnent également de cette façon, par exemple collections.OrderedDict.
txsaw1
2
Oh. C'est bien. Et vous n'avez pas à initialiser à '= []'. Bon produit!
Wilmer E. Henao
1
NameError: name 'a' is not defined
S Andrew
52

Vous pouvez le construire avec une compréhension de liste comme ceci:

>>> dict((i, range(int(i), int(i) + 2)) for i in ['1', '2'])
{'1': [1, 2], '2': [2, 3]}

Et pour la deuxième partie de votre question, utilisez defaultdict

>>> from collections import defaultdict
>>> s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
>>> d = defaultdict(list)
>>> for k, v in s:
        d[k].append(v)

>>> d.items()
[('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]
Nadia Alramli
la source
32

Vous pouvez utiliser setdefault:

d = dict()
a = ['1', '2']
for i in a:
    for j in range(int(i), int(i) + 2): 
        d.setdefault(j, []).append(i)

print d  # prints {1: ['1'], 2: ['1', '2'], 3: ['2']}

Le plutôt bizarrement nommé setdefault fonction au dit "Obtenez la valeur avec cette clé, ou si cette clé n'est pas là, ajoutez cette valeur puis retournez-la."

Comme d'autres l'ont souligné à juste titre, defaultdictc'est un choix meilleur et plus moderne. setdefaultest toujours utile dans les anciennes versions de Python (antérieures à 2.5).

RichieHindle
la source
2
Cela fonctionne, mais il est généralement préférable d'utiliser defaultdict lorsqu'il est disponible.
David Z
@ David, oui, setdefault n'était pas le design le plus brillant, désolé - ce n'est presque jamais le meilleur choix. Je pense que nous (les committers Python) avons racheté notre réputation collective avec collections.defaultdict ;-).
Alex Martelli
@DavidZ, setdefault est différent de defaultdict, car il est plus flexible: sinon, comment spécifiez-vous différentes valeurs par défaut pour différentes clés de dictionnaire?
Alex Gidan
@AlexGidan C'est vrai, mais pas particulièrement pertinent pour cette question.
David Z
Cette réponse est également utile lorsque vous avez besoin d'un OrderedDict et d'une valeur par défaut.
nimcap
2

Votre question a déjà reçu une réponse, mais IIRC vous pouvez remplacer des lignes comme:

if d.has_key(scope_item):

avec:

if scope_item in d:

Autrement dit, des dréférences d.keys()dans cette construction. Parfois, ce defaultdictn'est pas la meilleure option (par exemple, si vous souhaitez exécuter plusieurs lignes de code après celles elseassociées à ce qui précède if), et je trouve la insyntaxe plus facile à lire.

spiffyman
la source
2

Personnellement, j'utilise simplement JSON pour convertir des choses en chaînes et inversement. Des cordes je comprends.

import json
s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
mydict = {}
hash = json.dumps(s)
mydict[hash] = "whatever"
print mydict
#{'[["yellow", 1], ["blue", 2], ["yellow", 3], ["blue", 4], ["red", 1]]': 'whatever'}
john ktejik
la source
1

le moyen le plus simple est:

a = [1,2]
d = {}
for i in a:
  d[i]=[i, ]

print(d)
{'1': [1, ], '2':[2, ]}
Henning Lee
la source