Coder Perfect

What’s the best method to have a function return several values? [closed]

Problem

In languages that support it, tupling is frequently the standard approach to return multiple values.

Consider the following 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 need four or five values returned? 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.

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}

(For the record, y0, y1, and y2 are just abstract identifiers.) As previously stated, meaningful IDs would be used in practice.)

We now have a technique for projecting out a specific member of the returned object. As an example,

result['y0']

There is, however, another possibility. Instead, we may return a specific structure. I’ve presented this in the context of Python, but it should apply to other languages as well. In fact, if you’re working in C, this might be your only option. Here’s how it 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.

The __slots__ attribute, which Python provides for tiny objects, is an added functionality. The class could be written as follows:

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

The Python Reference Manual says:

Return a class with automatically added extra methods, typing, and other handy tools using Python 3.7’s new dataclasses:

@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)

Bill the Lizard offers another proposal that I had overlooked:

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.

As far as I can see, using a list in this fashion gives you nothing in terms of the tuple. In Python, the only true distinction between lists and tuples is that lists are mutable whereas tuples are not.

I like to use lists for any number of elements of the same type and tuples for a definite number of elements of predetermined types, following the principles from functional programming.

The inevitable inquiry follows the lengthy preface. Which strategy do you believe is the most effective?

Asked by saffsd

Solution #1

For this reason, named tuples were introduced in version 2.6. For a similar builtin example, check os.stat.

>>> import collections
>>> Point = collections.namedtuple('Point', ['x', 'y'])
>>> p = Point(1, y=2)
>>> p.x, p.y
1 2
>>> p[0], p[1]
1 2

The NamedTuple class was added to the new type library in recent versions of Python 3 (3.6+, I believe) to make named tuples easier to build and more powerful. Typing is something I inherited. You can utilize docstrings, default values, and type annotations with NamedTuple.

(Example from the documentation):

class Employee(NamedTuple):  # inherit from typing.NamedTuple
    name: str
    id: int = 3  # default value

employee = Employee('Guido')
assert employee.id == 3

Answered by A. Coady

Solution #2

I find it simplest to deal using tuples for little tasks. I start putting things into logical structures when that becomes too difficult to handle (and not before), but I believe your suggested use of dictionaries and ReturnValue objects is incorrect (or too simplistic).

Returning a dictionary with keys such as “y0,” “y1,” “y2,” and so on has no advantage over tuples. Returning a ReturnValue instance with properties like.y0,.y1,.y2, and so on has no benefit over tuples. If you want to get anywhere, you’ll need to start naming things, which you can do using tuples anyway:

def get_image_data(filename):
    

[snip]

return size, (format, version, compression), (width,height) size, type, dimensions = get_image_data(x)

Beyond tuples, the only good technique, in my opinion, is to return genuine objects with suitable methods and properties, such as those returned by re.match() or open() (file).

Answered by too much php

Solution #3

Many of the responses indicate that you should return a collection of some kind, such as a dictionary or a list. You may omit the extra syntax and simply write out the return values, separated by commas. It’s worth noting that this actually yields a tuple.

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

gives:

True
False

Answered by Joe Hansen

Solution #4

The dictionary gets my vote.

If I create a function that returns more than 2-3 variables, I usually fold them into a dictionary. Otherwise, I have a tendency to lose track of the sequence and content of the items I’m returning.

Incorporating a’special’ structure also makes your code more difficult to understand. (It will have to be discovered by someone else sifting through the code.)

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 }

Answered by monkut

Solution #5

Using generators is another option:

>>> 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, in my opinion, tuples are preferable, unless the values being returned are candidates for encapsulation in a class.

Answered by rlms

Post is based on https://stackoverflow.com/questions/354883/best-way-to-return-multiple-values-from-a-function