Sometimes you might want to make a function behave differently if the caller is expecting one or several values (e.g. x=func() versus x,y=func()). The expecting() function lets the function implementer find out how many values the caller wants as a function result.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | import inspect,dis
def expecting():
"""Return how many values the caller is expecting"""
f = inspect.currentframe()
f = f.f_back.f_back
c = f.f_code
i = f.f_lasti
bytecode = c.co_code
instruction = ord(bytecode[i+3])
if instruction == dis.opmap['UNPACK_SEQUENCE']:
howmany = ord(bytecode[i+4])
return howmany
elif instruction == dis.opmap['POP_TOP']:
return 0
return 1
def cleverfunc():
howmany = expecting()
if howmany == 0:
print "return value discarded"
if howmany == 2:
return 1,2
elif howmany == 3:
return 1,2,3
return 1
def test():
cleverfunc()
x = cleverfunc()
print x
x,y = cleverfunc()
print x,y
x,y,z = cleverfunc()
print x,y,z
test()
|
The "multiple return values"-idiom is actually just tuples that are unpacked, so the expecting() function implementation relies on the actual bytecode having an UNPACK_SEQUENCE code right after the function call (or POP_TOP if the return value isn't used at all). It has been tested under Python 2.3.3 and no guarantees can be given that it works under any future Python version.
Cute, but don't do this. Functions are easiest to understand when they behave simply, not when they try to be clever.
Code like:
can be rewritten to be:
Neither "x, y, z = point = cleverfunc()" nor "point = x, y, z = cleverfunc()" work.
Evil! I tweaked your code a bit so it would work in all use cases, hopefully.