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, as the number of values returned grows, this becomes an issue. What if you need four or five values returned? You could keep tupling them, but it’s easy to lose track of which value belongs where. Unpacking them wherever you wish to receive them is also very unsightly.
The next logical step appears to be the implementation of some kind of’record notation.’ The natural method to achieve this in Python is to use 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']
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.
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, however, my least favorite method. I’m not sure if it’s because of my Haskell exposure, but mixed-type lists have always made me uncomfortable. The list in this example is not mixed type, but it could be in the future.
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.)
Use descriptive dictionary keys, such as ‘x-values list,’ if type lookup is a concern.
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