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.
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.
A parameter is a local variable. Reassigning that local variable changes what the parameter refers to, not what the caller's variable refers to.
Tracing a Slice Reassignment
What do you think happens?
After remove_first runs, what will numbers contain?
Reveal 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.
def remove_first(t):
t = t[1:]
numbers = [7, 8, 9]
remove_first(numbers)- 1At the function call, t refers to the list [7, 8, 9].
- 2Evaluating t[1:] creates a separate list [8, 9].
- 3The assignment t = t[1:] changes the local variable t.
- 4The function does not change the original list object and does not return the new list.
- 5After 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.
[7, 8, 9]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.
| Operation | What it does | Effect on the original list |
|---|---|---|
| append() | Modifies the existing list object | The change is visible to the caller |
| pop() | Modifies the existing list object | The change is visible to the caller |
| remove() | Modifies the existing list object | The change is visible to the caller |
| extend() | Modifies the existing list object | The change is visible to the caller |
| Slicing | Creates a new list | The original remains unchanged |
| The + operator | Creates a new list | The original remains unchanged |
The key distinction is whether the existing list object is modified or a separate list is created.
Choosing the Function’s Effect
Removing the First Item
Create one function that changes the supplied list and another function that produces a list without changing the supplied list.
- 1Modify in place
Use an in-place method when the function's purpose is to change the list supplied by the caller.
- 2Return a new list
Use slicing and return its result when the function's purpose is to produce a separate list while preserving the original.
- 3Make 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.
An in-place function changes the caller's list. A function that returns a slice leaves the caller's original list unchanged.
[8, 9]
[7, 8, 9]
[8, 9]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.
[4, 5, 6]
[5, 6]Common Mistakes
Common mistake Assuming that t = t[1:] edits the caller's list
What it looks likedef remove_first(t): t = t[1:]Why it failsSlicing creates a new list, and the assignment changes only the local parameter.
Do this insteadUse 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 likedef add_item(items): items.append(10)Why it failsappend()modifies the actual list object, so the caller can see the change.Do this insteadCreate 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 likedef remove_first(t): t = t[1:]Why it failsThe new list is assigned to a local parameter and is not made available to the caller.
Do this insteadReturn the new list and assign the returned value in the caller when that result is needed.
Practice the Decision
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.
| Question | Reassignment to a slice | In-place mutation |
|---|---|---|
| Does it create a new list? | Yes | No |
| Does it modify the existing list? | No | Yes |
| Does the caller see the original list changed? | No | Yes |
| What should the function do if the new list is needed? | Return it | The existing list is already changed |
Use this comparison to decide how a function should handle a list argument.
Key Takeaways
- 1A function parameter is a local variable that can initially refer to the same list object as the caller's variable.
- 2Reassigning a parameter to a slice changes the local parameter and does not modify the original list.
- 3Methods such as
append(),pop(),remove(), andextend()modify the existing list object, making their effects visible to the caller. - 4Slicing and the + operator create new lists, so return the new list when the caller needs it.
- 5Choose 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(), orextend()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.