thebjorn thebjorn - 4 years ago 142
Python Question

Is there any way to catch a "too many values to unpack" error?

This usage of my subclass of tuple:

class MyTuple(tuple):
def __new__(cls, columns=()):
return tuple.__new__(cls, tuple(columns))

a, b = MyTuple(columns=('hello', 'world', 42))


gives the following exception:

Traceback (most recent call last):
File "<stdin>", line 6, in <module>
ValueError: too many values to unpack


A
ValueError
can have many causes, so do I need to use the following to catch it?

try:
a, b = MyTuple(columns=('hello', 'world', 42))
except ValueError as e:
if not str(e).endswith('unpack'):
raise # false alarm
# handle unpacking error here..


that seems rather inelegant.. Is there any way I can override which exception the tuple unpacking raises?

update: the actual use case is as follows

>>> dice = FactSet()
>>> for i in range(1, 7):
... dice.add('dice', n=i)
...
>>> print dice.n + dice.n == 10 # give me all combinations that add up to 10
XPROD((dice(n=4), dice(n=6))
(dice(n=5), dice(n=5))
(dice(n=6), dice(n=4)))
>>> a, b = dice.n + dice.n == 10 # same as above, but unpack the individual die
>>> a
FactSet([
dice(n=4),
dice(n=5),
dice(n=6),
])
>>> b
FactSet([
dice(n=6),
dice(n=5),
dice(n=4),
])
>>> a, b = dice.n + dice.n == 13 # no result should probably raise a more specific exception?
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: need more than 0 values to unpack
>>> ab = dice.n + dice.n == 13 # this is much more awkward and forces you to deal with the error in-situ (which might not be what you wanted)
>>> if ab:
>>> a, b = ab

Answer Source

Instead of doing:

a, b = <expression>

Do:

value = <expression>
try:
    a, b = value
except ValueError:
    ...

This is the general approach for this kind of problem: when your statement may raise the same kind of exception for more than a reason, simply break the statement into multiple parts, isolating the "critical" part from the rest of the code.


Is there any way I can override which exception the tuple unpacking raises?

No. The exception is not raised by the tuple object, it is raised by the Python interpreter. When you do: a, b, ..., z = something, the interpreter is executing the following code behind the scenes:

it = iter(something)

try:
    a = next(it)
    b = next(it)
    ...
    z = next(it)
except StopIteration:
    raise ValueError('need more than X values to unpack')

try:
    next(it)
except StopIteration:
    pass
else:
    raise ValueError('too many values to unpack (expected Y)')

As you can see, the tuple (or the iterable, in the general case) is only being iterated. It does not know what's going on.

And as you can see by reading CPython's source code, those exceptions are hard-coded and cannot be "overridden".

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download