Defining Functions
6.5 Getting Results from a Function
6.5.2 Functions that Modify Parameters
Return values are the main way to send information from a function back to the part of the program that called the function. In some cases, functions can also communicate back to the calling program by making changes to the function parameters. Understanding when and how this is possible requires the mastery of some subtle details about how assignment works in Python and the effect this has on the relationship between the actual and formal parameters used in a function call.
Let’s start with a simple example. Suppose you are writing a program that manages bank accounts or investments. One of the common tasks that must be performed is to accumulate interest on an account (as we did in the future value program). We might consider writing a function that automatically adds the interest to the account balance. Here is a first attempt at such a function:
# addinterest1.py
def addInterest(balance, rate):
newBalance = balance * (1+rate) balance = newBalance
The intent of this function is to set the balance of the account to a value that has been updated by the amount of interest.
Let’s try out our function by writing a very small test program.
def test():
amount = 1000 rate = 0.05
addInterest(amount, rate) print(amount)
What to you think this program will print? Our intent is that5% should be added to amount, giving a result of1050. Here’s what actually happens:
>>>addinterest.test() 1000
As you can see, amount is unchanged! What has gone wrong?
Actually, nothing has gone wrong. If you consider carefully what we have discussed so far regarding functions and parameters, you will see that this is exactly the result that we should expect. Let’s trace the execution of this example to see what happens. The first two lines of the testfunction create two local variables called amount and rate which are given the initial values of1000 and 0.05, respectively.
Next, control transfers to the addInterest function. The formal parameters balance and rate are assigned the values from the actual parameters amount and rate. Remember, even though the name rate appears in both functions, these are two separate variables. The situation as addInterest begins to execute is shown in Figure 6.6. Notice that the assignment of parame-ters causes the variables balance and rate in addInterest to refer to the values of the actual parameters.
152 Chapter 6. Defining Functions
Figure 6.6: Transfer of control to addInterest .
Executing the first line of addInterest creates a new variable newBalance. Now comes the key step. The next statement in addInterest assigns balance to have the same value as newBalance.
The result is shown in Figure 6.7. Notice that balance now refers to the same value as newBalance, but this had no effect on amount in the test function.
def addInterest(balance, rate):
At this point, execution of addInterest has completed and control returns to test. The local variables (including parameters) in addInterest go away, but amount and rate in the test func-tion still refer to the initial values of 1000 and 0.05, respectively. Of course, the program prints the value of amount as1000.
To summarize the situation, the formal parameters of a function only receive the values of the actual parameters. The function does not have any access to the variable that holds the actual parameter; therefore, assigning a new value to a formal parameter has no effect on the variable that contains the actual parameter. In programming language parlance, Python is said to pass all
6.5. Getting Results from a Function 153 parameters by value.
Some programming languages (e.g., C++ and Ada), do allow variables themselves to be sent as parameters to a function. Such a mechanism is called passing parameters by reference. When a variable is passed by reference, assigning a new value to the formal parameter actually changes the value of the parameter variable in the calling program.
Since Python does not allow passing parameters by reference, an obvious alternative is to change our addInterest function so that it returns the newBalance. This value can then be used to update the amount in the test function. Here’s a working version:
# addinterest2.py
def addInterest(balance, rate):
newBalance = balance * (1+rate) return newBalance
def test():
amount = 1000 rate = 0.05
amount = addInterest(amount, rate) print(amount)
test()
You should easily be able to trace through the execution of this program to see how we get this output.
>>>addinterest2.test() 1050
Now suppose instead of looking at a single account, we are writing a program that deals with many bank accounts. We could store the account balances in a Python list. It would be nice to have an addInterest function that adds the accrued interest to all of the balances in the list. If balancesis a list of account balances, we can update the first amount in the list (the one at index 0) with a line of code like this:
balances[0] = balances[0] * (1 + rate)
Remember, this works because lists are mutable. This line of code essentially says, “multiply the value in the 0th position of the list by(1+rate) and store the result back into the 0th position of the list.” Of course, a very similar line of code would work to update the balance of the next location in the list; we just replace the 0s with 1s.
balances[1] = balances[1] * (1 + rate)
A more general way of updating all the balances in a list is to use a loop that goes through positions0, 1, . . . , length − 1. Here is a program that implements this idea.
154 Chapter 6. Defining Functions
amounts = [1000, 2200, 800, 360]
rate = 0.05
addInterest(amounts, rate) print(amounts)
test()
Take a moment to study this program. The test function starts by setting amounts to be a list of four values. Then the addInterest function is called sending amounts as the first parameter. After the function call, the value of amounts is printed out. What do you expect to see? Let’s run this little program and see what happens.
>>> addinterest3.test()
[1050.0, 2310.0, 840.0, 378.0]
Isn’t that interesting? In this example, the function seems to change the value of the amounts variable. But I just told you that Python passes parameters by value, so the variable itself (amounts) can’tbe changed by the function. So what’s going on here?
The first two lines of test create the variables amounts and rates, and then control transfers to the addInterest function. The situation at this point is depicted in Figure 6.8. Notice that the
[ , , , ] def test():
rate = 0.05 print(amounts)
amounts = [1000,2150,800,3275]
rate
Figure 6.8: Transfer of list parameter to addInterest .