4

this is an exercise where Item is a class, and when I run testAll I have a valueError. The yield is suppose to return just 2 values, the content of 2 bags:

> Traceback (most recent call last):
  File "<pyshell#63>", line 1, in <module>
    testAll()
  File "/Downloads/L18_code.py", line 101, in testAll
    pset1,pset2=yieldAllCombos(items)
ValueError: too many values to unpack

def buildItems():
return [Item(n,v,w) for n,v,w in (('clock', 175, 10),
                                  ('painting', 90, 9),
                                  ('radio', 20, 4),
                                  ('vase', 50, 2),
                                  ('book', 10, 1),
                                  ('computer', 200, 20))]
def yieldAllCombos(items):
"""
Generates all combinations of N items into two bags, whereby each item is in one or
zero bags.

Yields a tuple, (bag1, bag2), where each bag is represented as a list of which item(s)
are in each bag.
"""
N = len(items)
# enumerate the 3**N possible combinations
for i in xrange(3**N):
    combo1 = []
    combo2 = []
    for j in xrange(N):
        # test bit jth of integer i
        if (i >> j) % 3 == 1:
            combo1.append(items[j])
        elif (i>>j) % 3 == 2:
            combo2.append(items[j])        
    yield(combo1,combo2)
def testAll():
    items = buildItems()
    pset1,pset2=yieldAllCombos(items)
user1347096
  • 165
  • 2
  • 4
  • 10

3 Answers3

3

You yield two items at a time in your loop, so you have to catch two at a time:

for pset1, pset2 in yieldAllCombos(items):
    ...
Blender
  • 289,723
  • 53
  • 439
  • 496
  • I don't understand the yield? it is not like a return? What does the for pset1... do? – user1347096 May 01 '13 at 00:16
  • @user1347096: Read the answers to this question: http://stackoverflow.com/questions/231767/the-python-yield-keyword-explained – Blender May 01 '13 at 00:19
  • 2
    This is a red herring. The issue isn't two at a time vs. one at a time, it's trying to unpack the entire iterator (full of pairs, but that doesn't matter) at once. – abarnert May 01 '13 at 00:21
3

If you don't understand generators and the yield statement at all, see the Python Wiki page for a general overview.

The problem is that you're trying to assign the entire generator yieldAllCombos(items) to two variables. This only works if yieldAllCombos generates exactly two values, by calling yield exactly twice.

But that's not what yieldAllCombos generates. If you want to see what it does, just try this:

print(list(yieldAllCombos(items)))

You'll see that you got back a large list of 2-tuples, one for each time it calls yield in the loop. And this shouldn't be too surprising, since a function called yieldAllCombos ought to be giving you a lot of combos, right? If you wanted to unpack the whole thing, you'd have to unpack it into one variable for each 2-tuple in the list, not just two variables. But that's probably not what you want.

What you probably want to do is iterator over all of the 2-tuples, unpacking each one. So:

for pset1, pset2 in yieldAllCombos(items):
abarnert
  • 354,177
  • 51
  • 601
  • 671
0

Below is suggestion

def testAll():
    items = buildItems()
    gen=yieldAllCombos(items)
    for l in range(3**len(items)+0):
        pset = next(gen)
        pset1 = pset[0]
        pset2 = pset[1]
        print(str(len(pset1)) + '   ' + str(len(pset2)))
        for j in range(len(pset1)):
            print(str(pset1[j]))
        for k in range(len(pset2)):
            print(str(pset2[k]))

The above code will generate an output similar to the following

Pset 1 length : 4 Pset 2 length : 2
******** pset 1 ********
<painting, 90.0, 9.0>
<vase, 50.0, 2.0>
<book, 10.0, 1.0>
<computer, 200.0, 20.0>
******** pset 2 ********
<clock, 175.0, 10.0>
<radio, 20.0, 4.0>

Reference

Mahendra Gunawardena
  • 1,956
  • 5
  • 26
  • 45