The canonical way to return multiple values in languages that support it is often tupling.
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.
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 }
(edit- 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']
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(object):
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.
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, wheras 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.
After the lengthy preamble, comes the inevitable question. Which method (do you think) is best?
I've typically found myself going the dictionary route because it involves less set-up work. From a types perspective however, you might be better off going the class route, since that may help you avoid confusing what a dictionary represents. On the other hand, there are some in the Python community that feel implied interfaces should be preferred to explicit interfaces, at which point the type of the object really isn't relevant, since you're basically relying on the convention that the same attribute will always have the same meaning.
So, how do -you- return multiple values in Python?
Named tuples were added in 2.6 for this purpose. Also see os.stat for a similar builtin example.
>>> 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
In recent versions of Python 3 (3.6+, I think), the new typing
library got the NamedTuple
class to make named tuples easier to create and more powerful. Inheriting from typing.NamedTuple
lets you use docstrings, default values, and type annotations.
Example (From the docs):
class Employee(NamedTuple): # inherit from collections.NamedTuple
name: str
id: int = 3 # default value
employee = Employee('Guido')
assert employee.id == 3
namedtuple
is having a smaller memory footprint for mass results (long lists of tuples, such as results of DB queries). For individual items (if the function in question is not called often) dictionaries and classes are just fine as well. But namedtuples are a nice/nicer solution in this case as well. - Lutz Precheltp=collections.namedtuple('point','x,y')(1,2)
- Dave Xnamedtuple
definitions (each call creates a new one), creating the namedtuple
class is relatively expensive in both CPU and memory, and all class definitions intrinsically involve cyclic references (so on CPython, you're waiting for a cyclic GC run for them to be released). It also makes it impossible to pickle
the class (and therefore, impossible to use instances with multiprocessing
in most cases). Each creation of the class on my 3.6.4 x64 consumes ~0.337 ms, and occupies just under 1 KB of memory, killing any instance savings. - ShadowRanger
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 getImageData(filename):
[snip]
return size, (format, version, compression), (width,height)
size, type, dimensions = getImageData(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)
.
size, type, dimensions = getImageData(x)
and (size, type, dimensions) = getImageData(x)
? I.e., does wrapping left-hand-side of a tupled assignment make any difference? - Reb.Cabin(1)
is a int while (1,)
or 1,
is a tuple. - phil
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
type(f())
returns <class 'tuple'>
. - Igortuple
aspect explicit; it's not really important that you're returning a tuple
, this is the idiom for returning multiple values period. Same reason you omit the parens with the swap idiom, x, y = y, x
, multiple initialization x, y = 0, 1
, etc.; sure, it makes tuple
s under the hood, but there is no reason to make that explicit, since the tuple
s aren't the point at all. The Python tutorial introduces multiple assignment long before it even touches on tuple
s. - ShadowRanger
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 }
result = g(x); other_function(result)
- monkut
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.
yield
? - CoreDumpError
I prefer to use tuples whenever a tuple feels "natural"; coordinates are a typical example, where the separate objects can stand on their own, e.g. in one-axis only scaling calculations, and the order is important. Note: if I can sort or shuffle the items without an adverse effect to the meaning of the group, then I probably shouldn't use a tuple.
I use dictionaries as a return value only when the grouped objects aren't always the same. Think optional email headers.
For the rest of the cases, where the grouped objects have inherent meaning inside the group or a fully-fledged object with its own methods is needed, I use a class.
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.
>>> def func():
... return [1,2,3]
...
>>> a,b,c = func()
>>> a
1
>>> b
2
>>> c
3
Python's tuples, dicts, and objects offer the programmer a smooth tradeoff between formality and convenience for small data structures ("things"). For me, the choice of how to represent a thing is dictated mainly by how I'm going to use the structure. In C++, it's a common convention to use struct
for data-only items and class
for objects with methods, even though you can legally put methods on a struct
; my habit is similar in Python, with dict
and tuple
in place of struct
.
For coordinate sets, I'll use a tuple
rather than a point class
or a dict
(and note that you can use a tuple
as a dictionary key, so dict
s make great sparse multidimensional arrays).
If I'm going to be iterating over a list of things, I prefer unpacking tuple
s on the iteration:
for score,id,name in scoreAllTheThings():
if score > goodScoreThreshold:
print "%6.3f #%6d %s"%(score,id,name)
...as the object version is more cluttered to read:
for entry in scoreAllTheThings():
if entry.score > goodScoreThreshold:
print "%6.3f #%6d %s"%(entry.score,entry.id,entry.name)
...let alone the dict
.
for entry in scoreAllTheThings():
if entry['score'] > goodScoreThreshold:
print "%6.3f #%6d %s"%(entry['score'],entry['id'],entry['name'])
If the thing is widely used, and you find yourself doing similar non-trivial operations on it in multiple places in the code, then it's usually worthwhile to make it a class object with appropriate methods.
Finally, if I'm going to be exchanging data with non-Python system components, I'll most often keep them in a dict
because that's best suited to JSON serialization.
Generally, the "specialized structure" actually IS a sensible current state of an object, with its own methods.
class Some3SpaceThing(object):
def __init__(self,x):
self.g(x)
def g(self,x):
self.y0 = x + 1
self.y1 = x * 3
self.y2 = y0 ** y3
r = Some3SpaceThing( x )
r.y0
r.y1
r.y2
I like to find names for anonymous structures where possible. Meaningful names make things more clear.
+1 on S.Lott's suggestion of a named container class.
For python 2.6 and up, a named tuple provides a useful way of easily creating these container classes, and the results are "lightweight and require no more memory than regular tuples".
In languages like Python, I would usually use a dictionary as it involves less overhead than creating a new class.
However, if I find myself constantly returning the same set of variables, then that probably involves a new class that I'll factor out.
I would use a dict to pass and return values from a function:
Use variable form as defined in form.
form = {
'level': 0,
'points': 0,
'game': {
'name': ''
}
}
def test(form):
form['game']['name'] = 'My game!'
form['level'] = 2
return form
>>> print(test(form))
{u'game': {u'name': u'My game!'}, u'points': 0, u'level': 2}
This is the most efficient way for me and for processing unit.
You have to pass just one pointer in and return just one pointer out.
You do not have to change functions' (thousands of them) arguments whenever you make a change in your code.
"Best" is a partially subjective decision. Use tuples for small return sets in the general case where an immutable is acceptable. A tuple is always preferable to a list when mutability is not a requirement.
For more complex return values, or for the case where formality is valuable (i.e. high value code) a named tuple is better. For the most complex case an object is usually best. However, it's really the situation that matters. If it makes sense to return an object because that is what you naturally have at the end of the function (e.g. Factory pattern) then return the object.
As the wise man said:
Premature optimization is the root of all evil (or at least most of it) in programming.
y3
, but unless y3 is declared global, this would yieldNameError: global name 'y3' is not defined
perhaps just use3
? - hetepeperfan