Friday 17 July 2020

Ignore python multiple return value

Say I have a Python function that returns multiple values in a tuple:

def func():
    return 1, 2

Is there a nice way to ignore one of the results rather than just assigning to a temporary variable? Say if I was only interested in the first value, is there a better way than this:

x, temp = func()

Answers:


You can use x = func()[0] to return the first value, x = func()[1] to return the second, and so on.

If you want to get multiple values at a time, use something like x, y = func()[2:4].


Answers:


One common convention is to use a '_' as a variable name for the elements of the tuple you wish to ignore. For instance:

def f():
    return 1, 2, 3

_, _, x = f()

Answers:


Three simple choices.

Obvious

x, _ = func()

x, junk = func()

Hideous

x = func()[0]

And there are ways to do this with a decorator.

def val0( aFunc ):
    def pick0( *args, **kw ):
        return aFunc(*args,**kw)[0]
    return pick0

func0= val0(func)

Answers:


Remember, when you return more than one item, you're really returning a tuple. So you can do things like this:

def func():
    return 1, 2

print func()[0] # prints 1
print func()[1] # prints 2

Answers:


This seems like the best choice to me:

val1, val2, ignored1, ignored2 = some_function()

It's not cryptic or ugly (like the func()[index] method), and clearly states your purpose.


Answers:


If you're using Python 3, you can you use the star before a variable (on the left side of an assignment) to have it be a list in unpacking.

# Example 1: a is 1 and b is [2, 3]

a, *b = [1, 2, 3]

# Example 2: a is 1, b is [2, 3], and c is 4

a, *b, c = [1, 2, 3, 4]

# Example 3: b is [1, 2] and c is 3

*b, c = [1, 2, 3]       

# Example 4: a is 1 and b is []

a, *b = [1]

Answers:


This is not a direct answer to the question. Rather it answers this question: 'How do I choose a specific function output from many possible options?'.

If you are able to write the function (ie, it is not in a library you cannot modify), then add an input argument that indicates what you want out of the function. Make it a named argument with a default value so in the 'common case' you don't even have to specify it.

    def fancy_function( arg1, arg2, return_type=1 ):
        ret_val = None
        if( 1 == return_type ):
            ret_val = arg1 + arg2
        elif( 2 == return_type ):
            ret_val = [ arg1, arg2, arg1 * arg2 ]
        else:
            ret_val = ( arg1, arg2, arg1 + arg2, arg1 * arg2 ) 
        return( ret_val )

This method gives the function 'advanced warning' regarding the desired output. Consequently it can skip unneeded processing and only do the work necessary to get your desired output. Also because Python does dynamic typing, the return type can change. Notice how the example returns a scalar, a list or a tuple... whatever you like!


Answers:


The common practice is to use the dummy variable _ (single underscore), as many have indicated here before.

However, to avoid collisions with other uses of that variable name (see this response) it might be a better practice to use __ (double underscore) instead as a throwaway variable, as pointed by ncoghlan. E.g.:

x, __ = func()

Answers:


If this is a function that you use all the time but always discard the second argument, I would argue that it is less messy to create an alias for the function without the second return value using lambda.

def func():
    return 1, 2

func_ = lambda: func()[0] 

func_()  # Prints 1 

Answers:


When you have many output from a function and you don't want to call it multiple times, I think the clearest way for selecting the results would be :

results = fct()
a,b = [results[i] for i in list_of_index]

As a minimum working example, also demonstrating that the function is called only once :

def fct(a):
    b=a*2
    c=a+2
    d=a+b
    e=b*2
    f=a*a
    print('fct called')
    return[a,b,c,d,e,f]

results=fct(3)
> fct called

x,y = [results[i] for i in [1,4]]

And the values are as expected :

results
> [3,6,5,9,12,9]
x
> 6
y
> 12

For convenience, Python list indexes can also be used :

x,y = [results[i] for i in [0,-2]]

Returns : a = 3 and b = 12


Answers:


The best solution probably is to name things instead of returning meaningless tuples (unless there is some logic behind the order of the returned items). You can for example use a dictionary:

def func():
    return {'lat': 1, 'lng': 2}
    
latitude = func()['lat']

You could even use namedtuple if you want to add extra information about what you are returning (it's not just a dictionary, it's a pair of coordinates):

from collections import namedtuple 

Coordinates = namedtuple('Coordinates', ['lat', 'lng'])

def func():
    return Coordinates(lat=1, lng=2)

latitude = func().lat

If the objects within your dictionary/tuple are strongly tied together then it may be a good idea to even define a class for it. That way you'll also be able to define interaction between objects of this type and give an API on how to work with them. A natural question that follows: When should I be using classes in Python?.


Answers:


No comments:

Post a Comment