I feel as if a return statement should only return one value. When it returns multiple, things get confusing. I know since the return type in Java has to be declared, this is difficult to do, but in languages like Python it is a snap.
Are multiple return values a sign of bad design or coding style?
Python example
def get_name():
# you code
return first_name, last_name
7 Answers 7
It's not a sign of anything, and is not neither good nor bad design or coding style.
Returning multiple values can actually be appropriate and allow to write less code. Let's take an example of a method which takes a string like "-123abc"
and converts it to an integer like -123
:
(bool, int) ParseInteger(string text)
{
// Code goes here.
}
returns both:
- a value indicating whether the operation was a success,
- the number converted from string.
How can we refactor this?
1. Exceptions
We can add exceptions, if the language supports them. Remember than in most languages, exceptions are expensive in resources. It means that if you have to deal with lots of non-numbers, it's better to avoid to throw an exception every time the string cannot be converted to a number.
2. New class
We can create a class and return an instance of an object of this class.
For example:
class ParsedInteger
{
bool IsSuccess { get; set; }
int Number { get; set; }
}
Is it easier to understand? Shorter to write? Does it bring anything? I don't think so.
3. Out parameters
If the language supports it, we can also use out
parameters. This is the approach of C# where returning multiple values is not possible. For example, when parsing a number, we use: bool isSuccess = int.TryParse("-123abc", out i)
. I'm not sure how is it better to use out
parameters compared to multiple values. The syntax is not obvious, and even StyleCop itself (the tool used to enforce the default Microsoft style rules on the code) complains about those parameters, suggesting to remove them when possible.
Finally, in languages as C# where there is no such a thing as returning multiple values, things are progressively added to imitate the behavior. For example, Tuple
was added to allow returning several values without having to write your own class or use out
parameters.
-
1+1: "It's not a sign of anything." It is an option. The option you pick should depend on the information you are trying to return.unholysampler– unholysampler2011年07月26日 19:29:58 +00:00Commented Jul 26, 2011 at 19:29
-
1+1 I hate using out parameters because it's much less intuitive to track a reference throughout the code block. Also, it's pretty common practice to assume that a returned value means success (and a -1 is a failure) but your example using a tuple perfectly illustrates where both results can be checked without requiring any assumptions about the internal implementation. We could probably fix a lot of potential bugs if we just made a commitment to quit using (1 == !undefined||true||success) and (-1 == undefined||false||failure); the grandaddy of leaky abstractions.Evan Plaice– Evan Plaice2012年01月17日 05:14:40 +00:00Commented Jan 17, 2012 at 5:14
When your function returns a reference to an object that contains multiple members, is it returning one value or many? In the example you show, the function is actually returning an object of type tuple. Python just happens to support syntactic 'sugar' so that you don't have to explicitly dereference the members when making assignments from the return value of the function.
Perhaps you would be better off to encapsulate these values into an object - since who knows when you'll decide to return even more data, right? :)
Well, I've seen this recently:
current_pixel = pixel[x,y]
# Formula for Orange: 50% red and more green than blue
if float(current_pixel[0])/(current_pixel[0]+current_pixel[1]+current_pixel[2]+1)>0.5 and current_pixel[1]>current_pixel[2]:
And here's the sane way to do it:
red, green, blue = pixel[x,y]
if green > blue and float(red) / (red + green + blue + 1) > 0.5:
-
3If you had a pixel object with
pixel.red
,pixel.green
,pixel.blue
, this would be even more clear - no way to mix the order of the pixel's components.Paŭlo Ebermann– Paŭlo Ebermann2011年07月26日 22:06:52 +00:00Commented Jul 26, 2011 at 22:06 -
2@Paŭlo Ebermann Isn't the relationship above clear enough to intuit. What value do you add to the codebase by including more boilerplate? Where does the structure go? Is it's functionality tested? In statically typed languages like C# where boilerplate is the glue that holds everything together a class-based approach is perfectly acceptable. In dynamically typed languages it's more difficult to track errors so the simplicity adds value.Evan Plaice– Evan Plaice2012年01月17日 05:04:17 +00:00Commented Jan 17, 2012 at 5:04
-
@Paŭlo Ebermann: It's because the library (PIL) works that way. But you're correct. Would it be my own library, it'd probably have a Pixel class.pillmuncher– pillmuncher2012年03月04日 00:55:27 +00:00Commented Mar 4, 2012 at 0:55
Well, I hope it's not a sign of bad design or coding style as it's common in the built in libraries. Your example of first_name, last_name looks perfectly reasonable to me. Once you go beyond a few reasonable and related values though, it probably makes sense to look into other alternatives so as to avoid returning a huge list of positional values.
I'm assuming you're talking about returning something like a tuple, object, or other composite data structures. Using them can often make a program more clear, but you have to be careful about cohesion and coupling. I've seen instances where a huge data structure is being passed around that could easily be replaced by globals, when different parts of the code should have been responsible for small, consistent pieces of it.
No, I don't think you can declare this to be bad style just because you think it's 'confusing'. Actually, why should it be more confusing than having multiple parameters when calling the function. It's more what you are used to and if you use it often enough, you won't find it difficult anymore.
return x, y, z