views:

145

answers:

5

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

+6  A: 
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)
Jonathan Feinberg
+1 doesn't directly answer the question but can certainly make the OP's life easier here.
Chris Lutz
Nice! That's a great idea.
Ellie P.
+7  A: 

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
sth
Thank you. Of course, duh.
Ellie P.
A: 

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
Dejw
Alternately, `for key, value in word_dict.iteritems():` is a bit longer, but nicer on the access of `key` and `value` IMHO.
Chris Lutz
+2  A: 

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.

unutbu
A: 

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 (): 
zenbot