Function Parameters and Scope
Reassigning a function parameter (e.g., t = t[1:]) does not modify the original list passed to the function; it only changes what the local 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
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)
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 |
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.
Modify in place: Use an in-place method when the function's purpose is to change the list supplied by the caller.
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.
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.
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
Assuming that t = t[1:] edits the caller's list
Slicing creates a new list, and the assignment changes only the local parameter.
Fix:
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.Using an in-place method when the original list should remain unchanged
append() modifies the actual list object, so the caller can see the change.
Fix:
Create and return a new list when preservation of the original is part of the function's purpose.Creating a new list but not returning it
The new list is assigned to a local parameter and is not made available to the caller.
Fix:
Return 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 |
Key Takeaways
- A function parameter is a local variable that can initially refer to the same list object as the caller's variable.
- Reassigning a parameter to a slice changes the local parameter and does not modify the original list.
- Methods such as append(), pop(), remove(), and extend() modify the existing list object, making their effects visible to the caller.
- Slicing and the + operator create new lists, so return the new list when the caller needs it.
- Choose in-place modification or a returned new list according to the function's purpose, and make that choice clear through naming and documentation.
Key Takeaways
- 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.