Advertisement
Advertisement


How do I return multiple values from a function?


Question

The canonical way to return multiple values in languages that support it is often tupling.

Option: Using a tuple

Consider this trivial example:

def f(x):
  y0 = x + 1
  y1 = x * 3
  y2 = y0 ** y3
  return (y0, y1, y2)

However, this quickly gets problematic as the number of values returned increases. What if you want to return four or five values? Sure, you could keep tupling them, but it gets easy to forget which value is where. It's also rather ugly to unpack them wherever you want to receive them.

Option: Using a dictionary

The next logical step seems to be to introduce some sort of 'record notation'. In Python, the obvious way to do this is by means of a dict.

Consider the following:

def g(x):
  y0 = x + 1
  y1 = x * 3
  y2 = y0 ** y3
  return {'y0': y0, 'y1': y1 ,'y2': y2}

(Just to be clear, y0, y1, and y2 are just meant as abstract identifiers. As pointed out, in practice you'd use meaningful identifiers.)

Now, we have a mechanism whereby we can project out a particular member of the returned object. For example,

result['y0']

Option: Using a class

However, there is another option. We could instead return a specialized structure. I've framed this in the context of Python, but I'm sure it applies to other languages as well. Indeed, if you were working in C this might very well be your only option. Here goes:

class ReturnValue:
  def __init__(self, y0, y1, y2):
     self.y0 = y0
     self.y1 = y1
     self.y2 = y2

def g(x):
  y0 = x + 1
  y1 = x * 3
  y2 = y0 ** y3
  return ReturnValue(y0, y1, y2)

In Python the previous two are perhaps very similar in terms of plumbing - after all { y0, y1, y2 } just end up being entries in the internal __dict__ of the ReturnValue.

There is one additional feature provided by Python though for tiny objects, the __slots__ attribute. The class could be expressed as:

class ReturnValue(object):
  __slots__ = ["y0", "y1", "y2"]
  def __init__(self, y0, y1, y2):
     self.y0 = y0
     self.y1 = y1
     self.y2 = y2

From the Python Reference Manual:

The __slots__ declaration takes a sequence of instance variables and reserves just enough space in each instance to hold a value for each variable. Space is saved because __dict__ is not created for each instance.

Option: Using a dataclass (Python 3.7+)

Using Python 3.7's new dataclasses, return a class with automatically added special methods, typing and other useful tools:

@dataclass
class Returnvalue:
    y0: int
    y1: float
    y3: int

def total_cost(x):
    y0 = x + 1
    y1 = x * 3
    y2 = y0 ** y3
    return ReturnValue(y0, y1, y2)

Option: Using a list

Another suggestion which I'd overlooked comes from Bill the Lizard:

def h(x):
  result = [x + 1]
  result.append(x * 3)
  result.append(y0 ** y3)
  return result

This is my least favorite method though. I suppose I'm tainted by exposure to Haskell, but the idea of mixed-type lists has always felt uncomfortable to me. In this particular example the list is -not- mixed type, but it conceivably could be.

A list used in this way really doesn't gain anything with respect to the tuple as far as I can tell. The only real difference between lists and tuples in Python is that lists are mutable, whereas tuples are not.

I personally tend to carry over the conventions from functional programming: use lists for any number of elements of the same type, and tuples for a fixed number of elements of predetermined types.

Question

After the lengthy preamble, comes the inevitable question. Which method (do you think) is best?

2020/06/20
1
1092
6/20/2020 9:12:55 AM


For small projects I find it easiest to work with tuples. When that gets too hard to manage (and not before) I start grouping things into logical structures, however I think your suggested use of dictionaries and ReturnValue objects is wrong (or too simplistic).

Returning a dictionary with keys "y0", "y1", "y2", etc. doesn't offer any advantage over tuples. Returning a ReturnValue instance with properties .y0, .y1, .y2, etc. doesn't offer any advantage over tuples either. You need to start naming things if you want to get anywhere, and you can do that using tuples anyway:

def get_image_data(filename):
    [snip]
    return size, (format, version, compression), (width,height)

size, type, dimensions = get_image_data(x)

IMHO, the only good technique beyond tuples is to return real objects with proper methods and properties, like you get from re.match() or open(file).

2019/10/10

A lot of the answers suggest you need to return a collection of some sort, like a dictionary or a list. You could leave off the extra syntax and just write out the return values, comma-separated. Note: this technically returns a tuple.

def f():
    return True, False
x, y = f()
print(x)
print(y)

gives:

True
False
2018/04/03

I vote for the dictionary.

I find that if I make a function that returns anything more than 2-3 variables I'll fold them up in a dictionary. Otherwise I tend to forget the order and content of what I'm returning.

Also, introducing a 'special' structure makes your code more difficult to follow. (Someone else will have to search through the code to find out what it is)

If your concerned about type look up, use descriptive dictionary keys, for example, 'x-values list'.

def g(x):
  y0 = x + 1
  y1 = x * 3
  y2 = y0 ** y3
  return {'y0':y0, 'y1':y1 ,'y2':y2 }
2008/12/10

Another option would be using generators:

>>> def f(x):
        y0 = x + 1
        yield y0
        yield x * 3
        yield y0 ** 4


>>> a, b, c = f(5)
>>> a
6
>>> b
15
>>> c
1296

Although IMHO tuples are usually best, except in cases where the values being returned are candidates for encapsulation in a class.

2014/02/23

I prefer:

def g(x):
  y0 = x + 1
  y1 = x * 3
  y2 = y0 ** y3
  return {'y0':y0, 'y1':y1 ,'y2':y2 }

It seems everything else is just extra code to do the same thing.

2019/06/20

Source: https://stackoverflow.com/questions/354883
Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Email: [email protected]