Comet has found a problem with the maker space scale: it reads two grams heavy. She writes trim(sample) to fix each Sample, and it works.
Then she writes swap_in(sample), which is meant to replace a sample with a fresh one. It runs without an error, and nothing happens.
"Same kind of function," she says, frowning. "Why does one change things and the other does not?"
Wren copies both functions onto graph paper. "What do you notice?" he asks. "One changes the object. The other changes the name."
Nova draws an arrow from s and a second arrow from sample. "Would you like a hint?" she asks. "Watch where each arrow points as the function runs."
Python passes arguments by assignment. The parameter becomes a new name for the same object the caller passed in.
So a function can change the object through its parameter, and the caller sees the change.
But if the function assigns a new object to the parameter, only that name moves. The caller still refers to the old object.
# args.py
from sample import Sample
def trim(sample):
sample.grams = sample.grams - 2
def swap_in(sample):
sample = Sample("S09", "chalk", 25)
sample.grams = 0
s = Sample("S01", "basalt", 42, 0, 2)
trim(s)
print(s.describe())
swap_in(s)
print(s.describe()) | Line that runs | s refers to | sample refers to | S01 grams |
|---|---|---|---|
| s = Sample("S01", ...) | S01 object | no name yet | 42 |
| trim(s) starts | S01 object | S01 object | 42 |
| sample.grams = sample.grams - 2 | S01 object | S01 object | 40 |
| swap_in(s) starts | S01 object | S01 object | 40 |
| sample = Sample("S09", ...) | S01 object | new S09 object | 40 |
| sample.grams = 0 | S01 object | S09 object, now 0 g | 40 |
S01 basalt 40 g at (0, 2) S01 basalt 40 g at (0, 2)
Lists behave the same way. Appending through the parameter changes the caller's list. Assigning a new list to the parameter does not.
# list_args.py
def add_spot(spots):
spots.append("S04")
def new_list(spots):
spots = ["S99"]
route = ["S01", "S02"]
add_spot(route)
print(route)
new_list(route)
print(route)
print(len(route)) ['S01', 'S02', 'S04'] ['S01', 'S02', 'S04'] 3
If a function makes a new object that the caller needs, it should return it, and the caller stores it. Here swap_in ends with return sample, and the call is s = swap_in(s).
# args_return.py
from sample import Sample
def swap_in(sample):
sample = Sample("S09", "chalk", 25)
sample.grams = 0
return sample
s = Sample("S01", "basalt", 42, 0, 2)
s = swap_in(s)
print(s.describe()) | Statement | True or false? |
|---|---|
| A function can change an object that is passed to it. | ? |
| Assigning a new object to a parameter also moves the caller's name. | ? |
| A parameter is a new name for the object the caller passed. | ? |
| To hand a new object back, a function can return it. | ? |
Nicely traced. Tomorrow Pip gets a cargo bay: a Rover that holds Samples.