简体   繁体   中英

Find out into how many values a return value will be unpacked

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)

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM