Python dictionary that maps strings to a set of strings?

62,486

Solution 1

set.add() does not return a new set, it modifies the set it is called on. Use it this way:

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

Also, if you use a for loop to iterate over a dict, you are iterating over the keys:

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

Alternatively you could iterate over word_dict.items() or word_dict.iteritems():

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

Solution 2

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)

Solution 3

When you say word_dict["foo"].add("baz"), you are adding 'baz' to the set word_dict["foo"].

The function returns None -- updating the set is a side-effect. So

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

ultimately sets word_dict["foo"] to None.

Just word_dict["foo"].add("baz") would be correct.

In the second scenario, when you say

for key, value in word_dict:                                                       

you run into an error because the correct syntax is

for key in word_dict: 

to loop over just the keys in word_dict. In this situation, you want

for key,value in word_dict.iteritems(): 

instead.

Solution 4

Try:

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]

It looks like standard dictionary iterator returns only key but not tuple.

Proof:

>>> 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

Solution 5

The problem is this:

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

When you call word_dict["foo"].add("baz"), you're mutating the set that word_dict["foo"] refers to, and that operation returns None. So reading your statement right to left, you're adding "baz" to the set refered to by word_dict["foo"], and then setting the result of that operation (that is, None) to word_dict["foo"].

So, to make this work as you expect, just remove word_dict["foo"] = from your statement.

Dictionaries iterate on their keys by default, hence the ValueError you try this:

for key, value in word_dict: 

What's happening here is that iterating on word_dict is returning a key only (say, "foo"), which you're then trying to unpack into the variables key & value. Unpacking "foo" gives you "f", "o", & "o", which is one value too many to fit into two variables, and hence your ValueError.

As others have stated, what you want is to iterate on the dictionary's key-value pairs, like so:

for key, value in word_dict.iteritems (): 
Share:
62,486
Ellie P.
Author by

Ellie P.

I'm a CS student and usability geek working on a Cocoa research project.

Updated on July 09, 2022

Comments

  • Ellie P.
    Ellie P. almost 2 years

    I would like to be able to make a Python dictionary with strings as keys and sets of strings as the values. E.g.: { "crackers" : ["crunchy", "salty"] } It must be a set, not a list.

    However, when I try the following:

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

    I get:

    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'
    

    If I do this:

      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
    

    I get:

    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
    

    Any tips on how to coerce Python into doing what I'd like? I'm an intermediate Python user (or so I thought, until I ran into this problem.)

  • visual_learner
    visual_learner over 14 years
    +1 doesn't directly answer the question but can certainly make the OP's life easier here.
  • visual_learner
    visual_learner over 14 years
    Alternately, for key, value in word_dict.iteritems(): is a bit longer, but nicer on the access of key and value IMHO.
  • DrColossos
    DrColossos over 12 years
    +1 didn't know that.. I had the same problem as the OP and this is very nice and IMO more readable than the accepted answer.
  • zvi
    zvi over 4 years
    Python 3 renamed: dict.iteritems() to: dict.items()