I have a function, and when it is called, I'd like to know what the return value is going to be assigned to - specifically when it is unpacked as a tuple. So:
a = func() # n = 1
vs.
a, b, c = func() # n = 3
I want to use the value of n
in func
. There must be some magic with inspect
or _getframe
that lets me do this. Any ideas?
Disclaimer (because this seems to be neccessary nowadays): I know this is funky, and bad practice, and shouldn't be used in production code. It actually looks like something I'd expect in Perl. I'm not looking for a different way to solve my supposed "actual" problem, but I'm curious how to achive what I asked for above. One cool usage of this trick would be:
ONE, TWO, THREE = count()
ONE, TWO, THREE, FOUR = count()
with
def count():
n = get_return_count()
if not n:
return
return range(n)
The ValueError: too many values to unpack (expected 2)” occurs when you do not unpack all of the items in a list. A common mistake is trying to unpack too many values into variables. We can solve this by ensuring the number of variables equals the number of items in the list to unpack.
Conclusion. The “valueerror: too many values to unpack (expected 2)” error occurs when you do not unpack all the items in a list. This error is often caused by trying to iterate over the items in a dictionary. To solve this problem, use the items() method to iterate over a dictionary.
Both * and ** are the operators that perform packing and unpacking in Python. The * operator (quite often associated with args) can be used with any iterable (such as a tuple, list and strings), whereas the ** operator, (quite often associated with kwargs) can only be used on dictionaries.
Adapted from http://code.activestate.com/recipes/284742-finding-out-the-number-of-values-the-caller-is-exp/:
import inspect
import dis
def expecting(offset=0):
"""Return how many values the caller is expecting"""
f = inspect.currentframe().f_back.f_back
i = f.f_lasti + offset
bytecode = f.f_code.co_code
instruction = ord(bytecode[i])
if instruction == dis.opmap['UNPACK_SEQUENCE']:
return ord(bytecode[i + 1])
elif instruction == dis.opmap['POP_TOP']:
return 0
else:
return 1
def count():
# offset = 3 bytecodes from the call op to the unpack op
return range(expecting(offset=3))
Or as an object that can detect when it is unpacked:
class count(object):
def __iter__(self):
# offset = 0 because we are at the unpack op
return iter(range(expecting(offset=0)))
There is little magic about how Python does this.
Simply put, if you use more than one target name on the left-hand side, the right-hand expression must return a sequence of matching length.
Functions that return more than one value really just return one tuple. That is a standard Python structure, a sequence of a certain length. You can measure that length:
retval = func()
print len(retval)
Assignment unpacking is determined at compile time, you cannot dynamically add more arguments on the left-hand side to suit the function you are calling.
Python 3 lets you use a splat syntax, a wildcard, for capturing the remainder of a unpacked assignment:
a, b, *c = func()
c
will now be a list with any remaining values beyond the first 2:
>>> def func(*a): return a
...
>>> a, b, *c = func(1, 2)
>>> a, b, c
(1, 2, [])
>>> a, b, *c = func(1, 2, 3)
>>> a, b, c
(1, 2, [3])
>>> a, b, *c = func(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: need more than 1 value to unpack
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With