Grade by Grade
A. Programming Concepts and Skills · Arrays and Subprograms

Parameter Passing: Arguments, Defaults and Lists

A3.2 A1.6 A4.4About 70 minutes
Learning goals
  • I can match arguments to parameters by position and by keyword, and use default parameter values.
  • I can return more than one value from a function and unpack the results.
  • I can predict whether a function changes the caller's data when it receives a number or a list.
  • I can choose between a function that changes a list in place and one that returns a new list, and document the choice.

Words to know

parameter passing
The way argument values are given to a function's parameters when it is called.
positional argument
An argument matched to a parameter by its position: the first argument goes to the first parameter, and so on.
keyword argument
An argument written as name=value, matched to the parameter with that name, in any order.
default parameter value
A value written in the definition, e.g. member=False, used when the call leaves that argument out.
pass by value / pass by reference
In many languages, pass by value gives the function a copy of the data; pass by reference gives it access to the caller's own data. Python passes a reference to the object, so a function can change a list it is given but cannot make the caller's variable name a different object.
side effect
A change a function makes outside itself, such as modifying a list that was passed in or printing.
tuple unpacking
Assigning several returned values at once, e.g. lo, hi = min_max(temps).

Getting the right values into the right places

A ski hill's ticket price depends on the skier's age, the day of the week, and whether they have a membership. A function that calculates the price needs all three pieces of information. Parameter passing is how a call delivers them. Get the order wrong and a 15-year-old is charged as if they were "Sat" years old!

This lesson looks closely at how arguments reach parameters, how to give parameters sensible defaults, and what happens when the argument is a list.

Positional and keyword arguments

  • Positional: the arguments are matched to the parameters in order. In ticket_price(15, "Sat"), age gets 15 and day gets "Sat".
  • Keyword: write name=value. Order no longer matters, and the call explains itself: ticket_price(day="Sun", age=40).
  • You may mix them, but positional arguments must come first: ticket_price(40, day="Sun") is fine; ticket_price(age=40, "Sun") is a SyntaxError.

Default values

In the definition, member=False gives the parameter a default. If the call leaves it out, the default is used. Parameters with defaults must come after the ones without. The built-in round(number, ndigits=None) that you used in Using Built-in Functions and Modules works this way.

def ticket_price(age, day, member=False):agedaymemberparametersargumentsticket_price(15, "Sat")no argument:uses default Falseticket_price(day="Sun", age=40, member=True)keywords: any order
In ticket_price(15, "Sat") the arguments fill the parameters in order; member has no argument, so it uses its default, False. In the bottom call, keyword arguments are matched by name, so their order does not matter.
def ticket_price(age, day, member=False):
"""Return the price of a ski-hill day ticket in dollars."""
if age < 13:
price = 30
else:
price = 55
if day == "Sat" or day == "Sun":
price += 10
if member:
price = price * 0.8
return price
print(ticket_price(15, "Sat"))
print(ticket_price(8, "Tue"))
print(ticket_price(day="Sun", age=40, member=True))

Output: 65, 30, 52.0. A 15-year-old on Saturday pays 55 + 10. An 8-year-old on Tuesday pays 30. A 40-year-old member on Sunday pays (55 + 10) × 0.8 = 52.0. (The prices are made up for this example.)

Returning more than one value

A function can return several values separated by commas. Python packs them into a tuple (a fixed, read-only sequence, written with round brackets). The caller can unpack them into separate variables in one line.

def min_max(values):
"""Return the lowest and highest element of a non-empty list."""
lo = values[0]
hi = values[0]
for v in values:
if v < lo:
lo = v
if v > hi:
hi = v
return lo, hi
temps = [-14, -9, -22, -6, -11]
coldest, warmest = min_max(temps)
print(coldest, warmest)
print(min_max([5]))

Output: -22 -6 and then (5, 5). The second call shows the tuple itself, because it was printed instead of unpacked.

What happens to the caller's data?

When you pass an argument, the parameter becomes another name for the same object, much like b = a in Declaring, Initializing, Accessing and Modifying Lists. Two rules follow:

  1. Changing a list through the parameter (values[i] = ..., values.append(...)) changes the caller's list. They are the same list.
  2. Reassigning the parameter (n = n + 1 or items = []) only makes the parameter name refer to something new. The caller's variable is not affected. Numbers and strings cannot be changed in place at all, so a function can never change the caller's int, float or string variable.

Other languages describe this differently. In Java and C, a number argument is passed by value (the function gets a copy), and C++ lets you choose pass by reference with &. Python's approach is often called pass by object reference.

add_bonus(marks, 5)markscallervaluesfunction678093one shared list:changes are seenby the callerchange(n) with n = n + 1xcallernfunction45dashed: n named 4 when the call beganafter n = n + 1: n names 5, x still names 4
Left: the caller's marks and the parameter values name one list, so values[i] += 5 changes the caller's marks. Right: n = n + 1 makes the parameter n refer to a new value, 5; the caller's x still refers to 4.
def change(n):
n = n + 1
print("inside:", n)
x = 4
change(x)
print("outside:", x)
def clear(items):
items = [] # makes the parameter name a new empty list
snacks = ["chips", "fruit"]
clear(snacks)
print(snacks)

Output: inside: 5, outside: 4, ['chips', 'fruit']. Neither reassignment reached the caller. To empty the caller's list, the function would have to change the list itself, e.g. items.clear().

Worked example

Example 1: In place or new list?

A teacher wants to add bonus marks. Write two versions: add_bonus changes the list it is given; with_bonus returns a new list and leaves the original alone. Predict the output of the program below.

def add_bonus(values, bonus):

    """Add bonus to every element of values (changes the list)."""

    for i in range(len(values)):

        values[i] += bonus

def with_bonus(values, bonus):

    """Return a NEW list with bonus added; values is unchanged."""

    result = []

    for v in values:

        result.append(v + bonus)

    return result

marks = [62, 75, 88]

new_marks = with_bonus(marks, 3)

print(marks, new_marks)

add_bonus(marks, 5)

print(marks)

  1. with_bonus builds result and returns it. marks is untouched: [62, 75, 88]; new_marks is [65, 78, 91].
  2. add_bonus has no return (it is a procedure). It changes values[i], which is the caller's list, so marks becomes [67, 80, 93].
  3. Notice the docstrings: a function that changes its argument must say so, because the caller cannot tell from the call alone.

[62, 75, 88] [65, 78, 91] and then [67, 80, 93]

Worked example

Example 2: Swapped arguments, a silent logic error

def pace(distance_km, minutes): return minutes / distance_km gives the minutes per kilometre. A runner covers 5 km in 30 minutes, and the program calls pace(30, 5). What goes wrong, and how can keyword arguments help?

  1. Positional matching puts 30 into distance_km and 5 into minutes.
  2. The function returns 5 / 30 ≈ 0.17 minutes per km: a world-record-breaking 10 seconds per kilometre. No error message appears; it is a logic error.
  3. Tracing the call with the parameter names written in (distance_km = 30, minutes = 5) reveals the mistake.
  4. A call with keywords, pace(distance_km=5, minutes=30), cannot be swapped by accident, and gives the correct 30 / 5 = 6.0 minutes per km.

Arguments were in the wrong order. Use pace(5, 30) or, more safely, keyword arguments.

Just for fun

Why did the function refuse to change the caller's integer?

It said, "I only got a reference, and integers are immutable. I can look, but I can't touch."

Hands-on activity

Envelope parameters

You need: 3 envelopes, sticky notes, a pen, a sheet of paper with a short list written on it, and a computer with Python.

  1. Label the envelopes with the parameter names of ticket_price: age, day, member. Put a sticky note saying False in the member envelope; that is its default.
  2. A family member "calls" the function by reading arguments aloud, sometimes positionally ("15, Sat") and sometimes with keywords ("day equals Sun, age equals 40"). Put each value in the right envelope, then compute the price by following the code.
  3. Now the list: write [62, 75, 88] on paper. For add_bonus, put the actual paper in the envelope labelled values and change the numbers in pen. For with_bonus, copy the numbers onto a new paper. Which version leaves the original paper unchanged?
  4. Run both functions in Python and compare with your paper results.

What to look for: the student explains that handing over the paper itself is like passing a list, so in-place changes reach the caller.

Your turn

1

In def f(a, b, c=0): and the call f(4, 9), what are a, b and c?

2

Rewrite the call ticket_price(12, "Fri", True) using keyword arguments for every value.

3

Why is def f(x=1, y): an error?

4

Predict the output:

def grow(a):

    a.append(0)

b = [1]

grow(b)

grow(b)

print(b)

5

Predict the output:

def bump(n):

    n += 10

    return n

x = 1

y = bump(x)

print(x, y)

6

Write a function stats(values) that returns the total and the average of a list. Show how to unpack the result.

More practice: warm-up

1

What are arguments matched to when no keywords are used?

2

In round(7.256, ndigits=1), which argument is a keyword argument?

3

What type is returned by return 3, 4?

4

True or false: a function can change the elements of a list passed to it.

5

True or false: a function can change an integer variable of the caller by reassigning its parameter.

More practice: on your own

1

Write greet(name, greeting="Hello") that returns e.g. "Hello, Sam!". Show two calls: one using the default and one with "Bonjour".

2

Predict the output:

def first_and_last(a):

    return a[0], a[-1]

x, y = first_and_last(["Halifax", "Moncton", "Saint John"])

print(y, x)

3

Write a procedure replace_all(values, old, new) that changes every element equal to old into new, in place. Test it.

4

Predict the output:

def f(a):

    a = a + [4]

    return a

x = [1, 2]

y = f(x)

print(x, y)

5

def area(length, width) is called as area(width=3, length=10). What does it return if the body is return length * width? Is the call order a problem?

Challenge

1

Write remove_negatives(values) in two ways: (a) returning a new list, (b) changing the list in place. Why is (b) harder to write with a simple for loop over indexes?

2

Predict the output and explain:

def mystery(a, b=[]):

    b.append(a)

    return b

print(mystery(1))

print(mystery(2))

3

A function swap(a, b) with body a, b = b, a is meant to swap two variables of the caller. Explain why it cannot work in Python, and write a version that swaps two elements of a list instead.

Check your understanding

Quiz

1.

For def f(x, y=5):, what does f(2) set y to?

2.

Which call is a SyntaxError?

3.

def g(a): a[0] = 99 and nums = [1, 2], then g(nums). What is nums?

4.

def h(n): n = 0 and k = 7, then h(k). What is k?

5.

lo, hi = min_max([3, 8, 1]) where min_max returns lo, hi. What is hi?

6.

Why should a function that modifies its list argument say so in its docstring?

For parents and teachers

Big idea: A3.2 asks for subprograms that use parameter passing. Students learn positional and keyword arguments, defaults, multiple return values, and the key question: when does a function change the caller's data? A1.6 (modifying arrays) appears through list parameters, and tracing (A4.4) is used to catch swapped arguments.

How to help: The envelope activity makes the "same list vs. copy" idea concrete. Ask your teen to predict, before running, whether each function in the examples changes the original list.

Common difficulties:

  • Arguments in the wrong order. Suggest keyword arguments for functions with several parameters of the same type.
  • Expecting a function to change the caller's number. It must return the new value, and the caller must store it: x = bump(x).
  • Being surprised that a list was changed by a function. Use .copy() or a function that returns a new list when the original must be kept.
  • Using a list as a default value (b=[]), which is shared between calls. Use None instead.