What actually happens when returning multiple values in a function?

Question

In the context of this exercise, what actually happens when returning multiple values in a function?

Answer

In Python, when returning multiple values separated by commas, all the values listed are actually wrapped inside a data type known as a tuple, which looks like (1, 2, 3). That tuple is returned, containing each value.

This is also useful because you can do what is known as “tuple unpacking” – if a tuple has multiple values, you can assign the same number of variables to the number of elements in the tuple, so that each variable is assigned to each value in the order they appear in the tuple.

Example

# This is a simple tuple unpacking.
# Each variable is assigned to each 
# corresponding value.
arg1, arg2, arg3 = (1, 2, 3)

# This is a function that returns multiple values.
def times_ten(a, b, c):
  a = a * 10
  b = b * 10
  c = c * 10
  return a, b, c # This will return a tuple containing the values listed.

# Each variable will be assigned to each value in the returned tuple,
# in the order they appear.
new_a, new_b, new_c = times_ten(5, 6, 7)

# new_a = 50
# new_b = 60
# new_c = 70
32 Likes

I’m having trouble understanding the purpose of the “return” command to begin with.

In the example from the exercise:

def square_point(x_value, y_value):
  x_2 = x_value * x_value
  y_2 = y_value * y_value

Doesn’t this already assign (and store!) values to variables x_2 and y_2 as soon as the square_point function is called with concrete values of x and y?

What then is the added benefit of return x_2, y_2 in the third line of the function?

10 Likes

@ibagusa, I believe that the return command allows the programmer to use the returned values later on in the code, outside of the function. It is true that the example assigns a value to x_2 and to y_2 as soon as the function is called, but those values can’t be used outside of that function unless the function “returns” them. I’m sure my explanation is awkward and imprecise, but I hope it’s accurate.

7 Likes

The discussion at this link explains the purpose of the return command in python (versus print), and I think confirms my interpretation above:

1 Like

Functions that are not explicitly intended to PRINT, usually with some sort of special format, should never print, but always RETURN their value(s).

When there are two or more values they can be written in a comma separated list and will be received by the caller as a TUPLE.

>>> def foo(a, b, c):
    if a > b: a, b = b, a
    if a > c: a, c = c, a
    if b > c: b, c = c, b
    return a, b, c

>>> foo(9,8,7)
(7, 8, 9)
>>> 

Note that the return value is a tuple that must be unpacked if we wish to split out the values into separate variables.

>>> a, b, c = foo(9,8,7)
>>> a
7
>>> b
8
>>> c
9
>>> 
6 Likes

It’s because you only need to use return once and define the variables that you want to return beforehand.

Hi @mtf,

can you help us with: Difference between low, high and low_limit, high_limit?

I’m having a hard time understanding how, in this scenario the “return” knew to add the values to low, high without inclusion in the function or a process of defining them.

to be more specific, here is the code snippet:

def get_boundaries(target, margin):
  low_limit = target - margin
  high_limit = margin + target
  return(low_limit, high_limit)
low, high = get_boundaries(100, 20) 

print("Low limit: " + str(low) + ", " + "high limit: " + str(high))

How does the return know that low, high are now “low_limit” and “high_limit”?

3 Likes

Hello,

It’s the order that the return is placed:

  return(low_limit, high_limit)

Low first and high 2nd. When you wrote :

low, high = get_boundaries(100, 20) 

it followed the same order. Low gets assigned to low_limit value and high gets assigned the high_limit value. Switch high and low like below:

high, low = get_boundaries(100, 20)

The code doesn’t know high goes to high and low goes to low just because of the name, but because of the position of the return values.

This swap would give you:

Low limit: 120, high limit: 80

Which is wrong, but shows that it’s the order the code is written that makes it line up.

Not sure if this answers your question, but hopefully it helps.

14 Likes

Really helpful! Thank you @monochromaticmau!

1 Like

But the function stops at return. Which means, the variables low_limit and high_limit become empty. Then how are values assigned on print call? If the values of these two variables are now empty after function ends, how are they able to take the values after function stops?

1 Like

The function stops after it gets to return, but it actually returns a value: return(low_limit, high_limit).

Whenever you attempt to unpack the function, it assigns the items of the returned tuple to your variables in the order of appearance.

arg1, arg2 = get_boundaries(x, y)

will give you

arg1, arg2 = (low_limit, high_limit)

when you print arg1 it gives:
arg1 = low_limit

and printing arg2 gives:
arg2 = high_limit

Basically, it follows ‘First in, First out’.

The exact reason is called variable scope. Scope refers to the namespace for which a variable exists. In the BASIC programming language there was no concept of scope, and this complicated programs in a way you cannot imagine.

In Python and many other languages the default scope for all variables is local scope, meaning the level within the program where you define a variable is where that variable exists and no place else. This is not entirely correct 100% of the time, but for now you may understand the idea here.

Therefore, variables that are created inside the function definition, are local to the function and only that function. There lifetime is limited too, in the sense that they go away after the function returns/ends. This is important because as a programmer, you will probably want to use the same name for a variable in many different functions that you write that serve a similar purposes. In other words, you might always use the variable name rtn or ret or r when returning the result of a mathematical function for the sake of consistency, so when you read your programs/functions years from now you will already know what rtn, ret, or r could mean.

If variables did not have scope, and persisted forever, then all variable names would have to be unique throughout your entire program which would make coming up with useful names much more complicated. Bugs would be very difficult to find, because you would never be able to know which statement modified which variable and when.

With local scoped variables, you never need to think about or worry about this and you can be very consistent throughout all your different functions.

So, the reason you need to use the “return” statement is to make sure the correct local variables are passed back to the calling function. This helps with good coding practices too. Be careful where you put your return statements too; in other words, you should only have one per function and not 14 for example.

5 Likes

Why it doesn’t work when i am writing low and high variable separate.

Hi @method8611079156.

You’ll need to provide a little more information to know what you tried to know what didn’t work. Have a look at this FAQ for a good way to frame your question (you can ignore the bits about creating a new post for now).

At a guess you tried something like the following?

return low_limit high_limit

Which is not valid syntax. return will only return one object. As per the first post this is often a tuple which is created by using the two names low_limit, high_limit separated by the comma.

Or something with two returns?

return low_limit
return high_limit

When a return statement is executed the function itself is exited. No code grouped to this function will be executed following that return statement which includes other return statements. Which is why multiple returns are often sent back as a sequence.

Think of it this way…

*The def “defined function” is like a big warehouse.

*The equations and stuff underneath the “def” function is the workers and assembly lines making a product.

*The “return” at the end of the “def” function is like “FedEx” shipping out the finished product made from the “def” function. But the product’s destination is to another “function call” written below the “def” function.

It helped my knowledge regarding it. Nice explanation.

def get_boundaries(target, margin):

low_limit = target - margin

high_limit = margin + target

return low_limit, high_limit

low, high = get_boundaries(100,20)

#This statement saves the low_limt and high_limit from def get_boundaries

#To the variables low and high.

print(low)

print(high)