Concepts / Passing Arguments to Functions

Passing Arguments to Functions

A reference is the association between a variable name and an object. When you assign b = a, both variables refer to the same object—b is an alias for a.

  • Programming

One List, Two Names

Suppose a program gives a second variable the value of an existing list by writing b = a. It is easy to imagine that Python creates a second list. In fact, both names refer to one underlying object. The second name, b, is an alias for a. This distinction becomes especially important when the object is passed into a function and the function changes the object.

refers torefers toavariable name[1, 2, 3]one list objectbalias
What object do a and b refer to after b = a, and how are their references connected?

A reference is the association between a variable name and an object. Aliasing occurs when more than one variable name refers to the same object.

Tracing a List Mutation

What do you think happens?

If a and b refer to the same list and the first item is changed through b, what will a show?

  • a still shows the original first item
  • a shows the changed first item
  • a becomes a string
  • Python creates a second list automatically
Reveal answer

Answer: a shows the changed first item

The two names refer to one mutable list. Changing an item changes that one list object, so the change is visible through every alias.

Changing Through an Alias

A list is assigned to a, and b is then assigned from a. The first item is modified through b. Predict the list seen through a.

Create the list: a refers to one list containing 1, 2, and 3.

Create the alias: Writing b = a makes b refer to the same list. It does not create an independent list.

Mutate through b: Changing b[0] changes the first item of the single list object.

Inspect a: Because a still refers to that same list, it also shows the changed first item.

The list seen through a has the changed first item because a and b are aliases for one list.

same list after b[0] changesmutation through ba[1, 2, 3]a[9, 2, 3]b[1, 2, 3]b[9, 2, 3]
What happens to the list seen through every variable when the list is changed through one alias?

The important point is that the operation changes the list object, not merely the name used to reach it. Since there is only one underlying list, every alias reveals the updated contents. This is correct Python behavior, although it can be surprising when a programmer expected b = a to make a copy.

What Function Parameters Receive

When an object is passed to a function, the function parameter becomes another variable name associated with that object. If the argument is a list and the function mutates that list, the caller can observe the mutation through its original variable name. The parameter and the caller's variable can therefore act as aliases while the function is working with the object.

argument referencemutationcaller sees same objectitems[1, 2, 3]valuessame list object[9, 2, 3]changed listitems[9, 2, 3]
How does the list object and its reference move from the caller into the function parameter, and what does the caller observe after the function mutates it?

To reason about a function call, ask two questions: which object does the argument name refer to, and does the function mutate that object? If the answer to both questions involves the same mutable list, the caller's list can change as a result of the call. The function parameter is another route to the list, not automatically an independent list.

Passing a list to a function does not by itself create an independent list. A function that modifies the received list can change the object that the caller also refers to.

Lists and Strings Behave Differently

Aliasing is most troublesome with mutable objects. Lists are mutable, so their contents can be changed in place. If two names refer to the same list, a modification through one name is visible through the other. Strings are immutable, so their contents cannot be changed in place. An operation that appears to modify a string creates a new string instead.

same object changesalias sees mutationrebinding creates new stringa[1, 2][9, 2]in-place mutationabananabBANANAbsame listbbanana
Why does changing a list through one name affect its aliases, while assigning a new string does not change the original string?
Object typeCan contents change in place?Effect of an apparent change through one name
ListYesAll aliases see the changed list
StringNoA new string is created and the name can refer to it; another name still refers to the original string

Why String Aliasing Is Harmless Here

Two names refer to the string banana. The name b is then assigned the result of converting its string to uppercase. What does a refer to afterward?

Start with one string: Both a and b refer to the string banana.

Evaluate the uppercase operation: The operation does not modify banana in place because strings are immutable. It creates the string BANANA.

Rebind b: b is assigned the new string, while a continues to refer to banana.

a still refers to banana, and b refers to BANANA.

Preventing Shared-List Bugs

The safest approach is not to avoid assignment altogether. Instead, be intentional about whether two names should share one mutable list. If two independent lists are required, create an explicit copy rather than assigning the original list directly.

Two list-copying strategies given in the source are slicing, as in new_list = old_list[:], and the list constructor. Either strategy creates an independent list when that is what the program requires. The key debugging question is whether the second variable should share future mutations or should preserve its own contents.

direct assignment shares listexplicit copy separates listsoriginal[1, 2, 3]original[1, 2, 3]othersame listother[9, 2, 3]
Which variables unexpectedly share the same list, and where should a copy be made to prevent one update from changing another variable's data?

Mistakes with Aliases

  • Assuming b = a copies a list.

    Direct assignment creates another reference to the object; it does not create an independent list.

    Fix: Use slicing or the list constructor when independent lists are needed.

  • Expecting a list mutation to affect only the variable used for the mutation.

    There is only one underlying mutable object.

    Fix: Trace the object, not just the variable name, and check whether the names are aliases.

  • Treating a string operation as if it mutated the original string.

    Strings are immutable and cannot be changed in place.

    Fix: Keep track of which name is rebound and which name still refers to the original string.

  • Avoiding all assignment instead of deciding intentionally whether sharing is wanted.

    Aliasing is not inherently a Python error; it is a source of unexpected behavior when shared mutable state was not intended.

    Fix: Use direct assignment for intentional sharing and an explicit copy for independent list data.

Practice the Trace

MEDIUM

A caller has a list named scores. A second name, backup, is assigned from scores. A function receives backup and changes the first item of the received list. Predict whether scores shows that changed first item. Then decide whether the result would differ if backup had been created with an explicit list copy.

Hints
  • First determine whether scores and backup refer to one list or two.
  • Next determine whether the function changes the list object or creates a separate object.
  • Use the list-copying strategies described in this article when independent data is required.
  1. To solve an aliasing trace, follow the object references in order. Identify the original object, note every variable or parameter that refers to it, and then determine whether an operation mutates that object or rebinds one name to a new object.

Key Takeaways

  • Aliasing means that multiple variable names refer to the same object.
  • With a mutable list, a mutation through one alias is visible through every other alias, including a function parameter that refers to the list.
  • Strings are immutable, so an apparent change creates a new string instead of modifying the original string.
  • Use the is operator to check whether two names refer to the same object.
  • Use slicing or the list constructor to create an independent list when shared mutations are not intended.