Concepts / Function Parameters and Mutable Objects

Function Parameters and Mutable Objects

Aliasing occurs when two or more variables refer to the same object in memory. The assignment b = a creates an alias, not a copy.

  • Programming
Interactive lab

Try it: Names and Objects

How Python variables are names bound to objects: assignment never copies, mutating a list is seen through every name that points at it, and ints are replaced rather than changed.

How it works

  1. name = object binds a name; b = a makes b point at the same object.
  2. Mutating a list (append, +=) changes the one shared object.
  3. a = a + [x] and y = y + 1 create NEW objects and re-point one name.
  4. Passing a list to a function passes the reference, so the function can change it.

Default run (7 steps): Two names, one list. Every name is a label that points at an object. … Printed: [1, 2, 3] / True

Simplified: Six fixed scripts on a tiny heap; you choose the values. Object numbers are illustrative, not real id() values.

Educational simulation

Loading the simulation…

One List, Two Names

Suppose a program uses two variables, a and b, for what appears to be the same list. If changing the list through b also changes what you see through a, the program may not contain two lists at all. It may contain one list with two variable names referring to it. This relationship is called aliasing.

The assignment b = a creates an alias, not an independent copy. After that assignment, a and b refer to the same object.

refers torefers tosame objectavariable[1, 2, 3]one list objecta is bTruebvariable
What does the relationship look like when a and b both refer to the same list, and how can is show that they are the same object?

Checking Identity

Python provides the is operator for testing object identity. The expression a is b is True when both variables refer to the same object and False when they refer to different objects. This is different from ==. The == operator tests whether two objects have equal values. Two variables can therefore contain equal-looking lists while still referring to different list objects.

python
Output
True
True

Both tests are True in this example, but they answer different questions. The equality test says that the values match. The identity test says that a and b refer to the same list object. The identity result is the important clue when investigating aliasing.

Mutation Through an Alias

What do you think happens?

After b = a and b.append(4), what will a contain?

  • Only [1, 2, 3]
  • [1, 2, 3, 4]
  • a will no longer refer to a list
Reveal answer

Answer: [1, 2, 3, 4]

The assignment b = a gives both names access to one list. Because the list is mutable, modifying it through b changes the one shared object, which is also visible through a.

a = [1, 2, 3] b = a b.append(4) print(a) print(b)

refers torefers torefers torefers toa[1, 2, 3][1, 2, 3]one shared lista[1, 2, 3, 4][1, 2, 3, 4]same shared listb[1, 2, 3]b[1, 2, 3, 4]
What changes after the shared list is modified through b, and why does the change appear through a as well?

Parameters and Shared Lists

A function parameter can refer to the same mutable list that the caller supplies. If the function modifies that list through its parameter, the caller can observe the modification through its original variable. The parameter is another name referring to the object passed into the function; it does not automatically make an independent list copy.

python
Output
["old", "new"]
passed to functionused byvisible throughvalues["old"]itemssame listappend("new")modifies listvalues["old", "new"]
How does the caller's list become available through a function parameter, and why can a mutation inside the function affect the caller?

When debugging a function that unexpectedly changes a caller's list, inspect whether the function parameter and the caller's variable refer to the same object. The is operator helps verify that relationship.

Aliases and Independent Copies

If a program needs two lists whose later modifications are independent, create a copy explicitly. The source identifies a[:] and list(a) as ways to create an independent copy of a list. In contrast, b = a preserves the shared relationship.

OperationRelationshipEffect of modifying b
b = ab and a refer to the same listThe modification is visible through a
b = a[:]b refers to an independent list copyThe modification does not affect a
b = list(a)b refers to an independent list copyThe modification does not affect a
python
Output
[1, 2, 3, 4]
[1, 2, 3, 4]
[1, 2, 3, 5]
refers torefers toshared mutationshared mutationoriginal listnew listseparate mutationb = asame objecta[1, 2, 3]a[1, 2, 3][1, 2, 3, 4]both names show changeb[1, 2, 3]b = a[:]independent copyb[1, 2, 3][1, 2, 3, 5]copy changes separately
How do b = a and an explicit list copy differ in their object relationships and later behavior?

Tracing an Unexpected Change

Aliasing bugs often appear as an unexpected list change: one part of a program modifies a list, and another variable appears to change without being assigned. Trace the variables that refer to the list, then test likely pairs with is. If the test is True, a modification through either name affects the same mutable object.

identity checkidentity checksame objectsame objectoriginal["ready"]original is workingTrueoriginal["ready", "changed"]original is workingTrueworking["ready"]working["ready", "changed"]
How can comparing references before and after a mutation reveal why another variable changed unexpectedly?
  • Assuming b = a creates a second list.

    The assignment makes b refer to the same list object as a.

    Fix: Use b = a[:] or b = list(a) when an independent list is required.

  • Using == when the debugging question is whether two variables share one object.

    Equality compares values, not object identity.

    Fix: Use a is b to test whether the variables refer to the same object.

  • Forgetting that a function parameter can refer to the caller's mutable list.

    The parameter can refer to the same list supplied by the caller.

    Fix: Check the identity relationship and create a list copy when the function should work independently.

When a list must not be changed through another variable or function parameter, make the independence explicit with a[:] or list(a). When a change is unexpected, test whether the two variables are aliases before searching for a more complicated cause.

Practice the Trace

MEDIUM

A program contains a list named scores. Another variable receives scores with the assignment backup = scores. Before any modification, decide what scores is backup will return. Then decide what scores will contain after backup is modified. Finally, describe one change that would make backup independent from scores.

Hints
  • Ask whether the assignment creates an alias or a copy.
  • Use the difference between is and == to identify the relevant test.
  • An independent list can be created with a slice or the list constructor.

Tracing the Shared Object

Let scores = [10, 20], backup = scores, and then modify backup by adding 30. What identity relationship exists, and what values are visible through both variables?

Identify the assignment: The assignment backup = scores creates an alias rather than an independent copy.

Test identity: scores is backup is True because both variables refer to the same list object.

Apply the mutation: Adding 30 through backup modifies the shared list.

Inspect both names: Both scores and backup show [10, 20, 30] because each name refers to the modified object.

The variables are aliases. To prevent the shared change, create backup with scores[:] or list(scores).

Key Takeaways

  1. Aliasing means that multiple variables refer to the same object.
  2. The assignment b = a creates an alias, not a copy.
  3. Use is to test identity and == to test equality of values.
  4. A mutation to a shared list is visible through every variable that refers to that list, including a function parameter.
  5. Use a[:] or list(a) to create an independent list copy when later modifications must remain separate.

Key Takeaways

  • Aliasing occurs when two or more variables refer to one mutable object.
  • The is operator reveals whether two variables refer to the same object.
  • Mutating a shared list through one variable changes what other aliases observe.
  • Function parameters can refer to the caller's list and mutate that shared object.
  • Use a[:] or list(a) to create an independent list instead of an alias.