2010-02-18 3 views
13

Je voudrais pouvoir faire un dictionnaire Python avec des chaînes comme des clés et des ensembles de chaînes comme valeurs. E.g .: { "crackers" : ["crunchy", "salty"] } Ce doit être un ensemble, pas une liste.Dictionnaire Python qui mappe des chaînes à un ensemble de chaînes?

Cependant, quand je les opérations suivantes:

word_dict = dict() 
    word_dict["foo"] = set() 
    word_dict["foo"] = word_dict["foo"].add("baz")          
    word_dict["foo"] = word_dict["foo"].add("bang") 

Je reçois:

Traceback (most recent call last): 
    File "process_input.py", line 56, in <module> 
    test() 
    File "process_input.py", line 51, in test 
    word_dict["foo"] = word_dict["foo"].add("bang") 
AttributeError: 'NoneType' object has no attribute 'add' 

Si je fais ceci:

word_dict = dict() 
    myset = set() 
    myset.add("bar") 
    word_dict["foo"] = myset 
    myset.add("bang") 
    word_dict["foo"] = myset 

    for key, value in word_dict:              
     print key,                 
     print value 

Je reçois:

Traceback (most recent call last): 
    File "process_input.py", line 61, in <module> 
    test() 
    File "process_input.py", line 58, in test 
    for key, value in word_dict: 
ValueError: too many values to unpack 

Des astuces pour contraindre Python à faire ce que je veux? Je suis un utilisateur de Python intermédiaire (ou alors je pensais, jusqu'à ce que je suis tombé sur ce problème.)

Répondre

27

set.add() ne retourne pas une nouvelle set, il modifie le set il est appelé. Utilisez cette façon:

word_dict = dict() 
word_dict["foo"] = set() 
word_dict["foo"].add("baz")          
word_dict["foo"].add("bang") 

De plus, si vous utilisez une boucle for itérer sur un dict, vous itérez les clés:

for key in word_dict: 
    print key, word_dict[key] 

Sinon, vous pouvez itérer sur word_dict.items() ou word_dict.iteritems():

for key, value in word_dict.items(): 
    print key, value 
+0

Merci. Bien sûr, duh. –

23
from collections import defaultdict 

word_dict = defaultdict(set) 
word_dict['banana'].add('yellow') 
word_dict['banana'].add('brown') 
word_dict['apple'].add('red') 
word_dict['apple'].add('green') 
for key,values in word_dict.iteritems(): 
    print "%s: %s" % (key, values) 
+0

+1 ne répond pas directement à la question, mais peut certainement rendre la vie de l'OP plus facile ici . –

+0

Nice! C'est une bonne idée. –

+0

+1 ne savait pas que ... J'ai eu le même problème que l'OP et c'est très agréable et IMO plus lisible que la réponse acceptée. – DrColossos

1

Essayez:

word_dict = dict() 
myset = set() 
myset.add("bar") 
word_dict["foo"] = myset 
myset.add("bang") 
word_dict["foo"] = myset 

for key in word_dict:              
    print key, word_dict[key] 

Il semble que l'itérateur de dictionnaire standard renvoie seulement key mais pas un tuple.

Preuve:

>>> d = { 'test': 1 } 
>>> for k, v in d: print k, v 
... 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
ValueError: too many values to unpack 
>>> for k in d: print d[k] 
... 
1 
+0

Alternativement, 'pour la clé, la valeur dans word_dict.iteritems():' est un peu plus longue, mais plus agréable sur l'accès de 'key' et' value' IMHO. –

6

Quand vous dites word_dict["foo"].add("baz"), vous ajoutez 'baz' au word_dict set [ "foo"].

La fonction renvoie None - la mise à jour de l'ensemble est un effet secondaire. Alors

word_dict["foo"] = word_dict["foo"].add("baz") 

définit finalement word_dict["foo"] à None.

Juste word_dict["foo"].add("baz") serait correct.

Dans le deuxième scénario, quand vous dites

for key, value in word_dict:              

vous rencontrez une erreur, car la syntaxe correcte est

for key in word_dict: 

à boucle sur seulement les clés word_dict.Dans cette situation, vous voulez

for key,value in word_dict.iteritems(): 

à la place.

1

Le problème est le suivant:

word_dict["foo"] = word_dict["foo"].add("baz") 

Lorsque vous appelez word_dict["foo"].add("baz"), vous êtes l'ensemble que muter word_dict["foo"] fait référence, et qui renvoie le fonctionnement None. Donc en lisant votre déclaration de droite à gauche, vous ajoutez "baz" à l'ensemble référencé par word_dict["foo"], puis en définissant le résultat de cette opération (None) sur word_dict["foo"]. Donc, pour que cela fonctionne comme prévu, supprimez simplement word_dict["foo"] = de votre relevé.

Dictionnaires itérer sur leurs clés par défaut, d'où le ValueError vous essayez ceci:

for key, value in word_dict: 

Ce qui se passe ici est que itérer sur word_dict renvoie une seule clé (par exemple, « foo »), que vous » re essayez ensuite de décompresser dans les variables key & value. Déballer "foo" vous donne "f", "o", & "o", qui est une valeur de trop pour tenir dans deux variables, et donc votre ValueError.

Comme d'autres l'ont dit, ce que vous voulez est itérer sur les paires clé-valeur du dictionnaire, comme ceci:

for key, value in word_dict.iteritems(): 
Questions connexes