168

I am getting that exception from this code:

class Transaction:
    def __init__ (self):
        self.materials = {}

    def add_material (self, m):
        self.materials[m.type + m.purity] = m

    def serialize (self):
        ser_str = 'transaction_start\n'

        for k, m in self.materials:
            ser_str += m.serialize ()

        sert += 'transaction_end\n'
        return ser_str

The for line is the one throwing the exception. The ms are Material objects. Anybody have any ideas why?

johnsyweb
  • 136,902
  • 23
  • 188
  • 247
Nik
  • 7,113
  • 13
  • 51
  • 80

3 Answers3

192

self.materials is a dict and by default you are iterating over just the keys (which are strings).

Since self.materials has more than two keys*, they can't be unpacked into the tuple "k, m", hence the ValueError exception is raised.

In Python 2.x, to iterate over the keys and the values (the tuple "k, m"), we use self.materials.iteritems().

However, since you're throwing the key away anyway, you may as well simply iterate over the dictionary's values:

for m in self.materials.itervalues():

In Python 3.x, prefer dict.values() (which returns a dictionary view object):

for m in self.materials.values():
Community
  • 1
  • 1
johnsyweb
  • 136,902
  • 23
  • 188
  • 247
  • Something i didn't get here, why do you say it has more than 2 keys? – Nuno Furtado Mar 31 '14 at 11:05
  • 10
    @NunoFurtado: 2.5 years later, I can only guess my younger self's reasoning. Here goes! Were there zero or one keys in `self.materials`, I'd expect the `ValueError` message to be `need more than n values to unpack`. Were there exactly two keys, these would have been unpacked into `k` and `m` (and presumably a different error would occur on the next line). Three or more keys? `ValueError: too many values to unpack`, per the title. – johnsyweb Mar 31 '14 at 11:32
  • 1
    lol, hadn't even noticed the post age. i ran into a similar issue and i just don't get why that error would occur, none the less i used your solution to fix the problem – Nuno Furtado Mar 31 '14 at 16:04
83
for k, m in self.materials.items():

example:

miles_dict = {'Monday':1, 'Tuesday':2.3, 'Wednesday':3.5, 'Thursday':0.9}
for k, v in miles_dict.items():
    print("%s: %s" % (k, v))
Mona Jalal
  • 34,860
  • 64
  • 239
  • 408
Sunhwan Jo
  • 2,293
  • 1
  • 15
  • 13
19

Iterating over a dictionary object itself actually gives you an iterator over its keys. Python is trying to unpack keys, which you get from m.type + m.purity into (m, k).

My crystal ball says m.type and m.purity are both strings, so your keys are also strings. Strings are iterable, so they can be unpacked; but iterating over the string gives you an iterator over its characters. So whenever m.type + m.purity is more than two characters long, you have too many values to unpack. (And whenever it's shorter, you have too few values to unpack.)

To fix this, you can iterate explicitly over the items of the dict, which are the (key, value) pairs that you seem to be expecting. But if you only want the values, then just use the values.

(In 2.x, itervalues, iterkeys, and iteritems are typically a better idea; the non-iter versions create a new list object containing the values/keys/items. For large dictionaries and trivial tasks within the iteration, this can be a lot slower than the iter versions which just set up an iterator.)

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
  • Thanks. Your tip helped me to solve my problem, when updating a dictionary of the form { 'longkey_n' : [ 'value_1', 'value_n' ] } :-D. My program also complained about too many values... – Semo May 18 '17 at 10:32