Concepts / Function Parameters and Scope

Function Parameters and Scope

A parameter is a local variable. Reassigning that local variable changes what the parameter refers to, not what the caller's variable refers to.

  • Programming

One List, Two Variables

When a list is passed to a function, the function parameter can refer to the same list object that the caller's variable refers to. The important question is what the function does next. If the function changes the existing list object with an in-place method, the caller can see that change. If the function reassigns the parameter to a new list, the caller's variable continues to refer to the original list.

Key point

A parameter is a local variable. Reassigning that local variable changes what the parameter refers to, not what the caller's variable refers to.

items[10, 20, 30]t[1:][20, 30]t[10, 20, 30][10, 20, 30]original listrefers tobefore reassignmentafter t = t[1:]
What does the parameter refer to before and after reassignment, and what object does the caller's variable still refer to?

Tracing a Slice Reassignment

What do you think happens?

After remove_first runs, what will numbers contain?

  • [8, 9]

  • [7, 8, 9]

  • The function will return a new list automatically

Reveal answer
Answer:

[7, 8, 9]

The expression numbers[1:] creates a new list. The assignment changes the local parameter to refer to that new list, while the caller's variable still refers to the original list.

Code walkthrough
def remove_first(t):
    t = t[1:]

numbers = [7, 8, 9]
remove_first(numbers)
  1. 1
    At the function call, t refers to the list [7, 8, 9].
  2. 2
    Evaluating t[1:] creates a separate list [8, 9].
  3. 3
    The assignment t = t[1:] changes the local variable t.
  4. 4
    The function does not change the original list object and does not return the new list.
  5. 5
    After the call, numbers remains [7, 8, 9].
  • The parameter t initially refers to the same list as numbers.
  • The slice t[1:] creates a new list containing [8, 9].
  • The assignment makes t refer to the new list.
  • The caller's variable numbers still refers to the original list.
Output
[7, 8, 9]
t[7, 8, 9][8, 9]new listnumbers[7, 8, 9]t = t[1:]caller remains
What changes when the function assigns t = t[1:]?

Mutation and New Lists

List operations fall into two useful groups. In-place methods operate on the existing list object. Methods such as append() and pop() modify that object, so a caller can see the result when the function parameter refers to the caller's list. Other operations create a new list. Slicing and the + operator create new lists, leaving the original list unchanged unless the new result is assigned back to the caller's variable or returned and used.

OperationWhat it doesEffect on the original list
append()Modifies the existing list objectThe change is visible to the caller
pop()Modifies the existing list objectThe change is visible to the caller
remove()Modifies the existing list objectThe change is visible to the caller
extend()Modifies the existing list objectThe change is visible to the caller
SlicingCreates a new listThe original remains unchanged
The + operatorCreates a new listThe original remains unchanged

The key distinction is whether the existing list object is modified or a separate list is created.

items[1, 2, 3]items[1, 2, 3, 4]new list[2, 3]items[1, 2, 3]append(4)items[1:]original remains
Which operations change the existing list object, and which operations create a separate list object?

Choosing the Function’s Effect

Worked example

Removing the First Item

Create one function that changes the supplied list and another function that produces a list without changing the supplied list.

  1. 1
    Modify in place

    Use an in-place method when the function's purpose is to change the list supplied by the caller.

  2. 2
    Return a new list

    Use slicing and return its result when the function's purpose is to produce a separate list while preserving the original.

  3. 3
    Make the result explicit

    The caller can tell the two purposes apart because one function changes the argument and the other returns a result that the caller can store.

Result

An in-place function changes the caller's list. A function that returns a slice leaves the caller's original list unchanged.

python
Output
[8, 9]
[7, 8, 9]
[8, 9]
numbers[7, 8, 9]items[7, 8, 9]pop(0)in-place method[8, 9]same list objectfunction callitems.pop(0)modifies
How does a method such as append, extend, or remove move from the function parameter to the original list object?
Best practice

Choose the behavior that matches the function's purpose. If a function is intended to update a supplied list, use an in-place method and make that intention clear through its name and documentation. If it is intended to produce another list, return the new list and leave the original unchanged.

Two Lists After a Return

A function can create a new list with slicing and return it. In that case, the caller may keep both the original list and the returned list. The original remains available through the caller's variable, while the returned value refers to the newly created list. This is different from assigning a slice only to a local parameter and then discarding it when the function ends.

python
Output
[4, 5, 6]
[5, 6]
original[4, 5, 6][4, 5, 6]original listresult[5, 6][5, 6]new listrefers toreceives return value
What list objects exist after a function returns a slice, and how is the returned list related to the original?

Common Mistakes

  • Common mistake

    Assuming that t = t[1:] edits the caller's list

    What it looks like
    def remove_first(t):
        t = t[1:]
    Why it fails

    Slicing creates a new list, and the assignment changes only the local parameter.

    Do this instead

    Use an in-place method such as pop() when the function should change the supplied list, or return the slice when the function should create a result.

  • Common mistake

    Using an in-place method when the original list should remain unchanged

    What it looks like
    def add_item(items):
        items.append(10)
    Why it fails

    append() modifies the actual list object, so the caller can see the change.

    Do this instead

    Create and return a new list when preservation of the original is part of the function's purpose.

  • Common mistake

    Creating a new list but not returning it

    What it looks like
    def remove_first(t):
        t = t[1:]
    Why it fails

    The new list is assigned to a local parameter and is not made available to the caller.

    Do this instead

    Return the new list and assign the returned value in the caller when that result is needed.

Practice the Decision

MEDIUM

Write two functions for a list named values. The first function should remove the last item from the supplied list by modifying it in place. The second function should return a new list containing all items except the last, without modifying the supplied list.

Hints
  • Use an in-place list method for the first function.
  • Use slicing and return the result for the second function.
  • Test each function with a list and inspect both the original variable and any returned value.
QuestionReassignment to a sliceIn-place mutation
Does it create a new list?YesNo
Does it modify the existing list?NoYes
Does the caller see the original list changed?NoYes
What should the function do if the new list is needed?Return itThe existing list is already changed

Use this comparison to decide how a function should handle a list argument.

Key Takeaways

  1. 1
    A function parameter is a local variable that can initially refer to the same list object as the caller's variable.
  2. 2
    Reassigning a parameter to a slice changes the local parameter and does not modify the original list.
  3. 3
    Methods such as append(), pop(), remove(), and extend() modify the existing list object, making their effects visible to the caller.
  4. 4
    Slicing and the + operator create new lists, so return the new list when the caller needs it.
  5. 5
    Choose in-place modification or a returned new list according to the function's purpose, and make that choice clear through naming and documentation.

At a glance

  • Reassigning a function parameter does not reassign the caller's variable.
  • A slice creates a new list, while in-place methods modify the existing list.
  • Use methods such as append(), pop(), remove(), or extend() when a function should change the supplied list.
  • Return a sliced or otherwise newly created list when the original should remain unchanged.
  • Function names and documentation should make the intended list behavior clear.

Related concepts

More concepts starting with F