Intermediate Python

Mutable Function Arguments

Python has a variety of data types, some of which can be modified after their creation and others that can't. When we talk about types that can be modified, we refer to them as "mutable" types. In contrast, those that can't be changed are called "immutable" types.

Mutable Types

The most common mutable types are lists, dicts, and sets. Here's an example of a list:
fruits = ['apple', 'banana', 'cherry']
print(fruits, type(fruits))  # ['apple', 'banana', 'cherry'] <class 'list'>
With a list, you can change its elements, add new ones, or remove existing ones:
fruits[1] = 'blueberry'       # Change 'banana' to 'blueberry'
fruits.append('dragonfruit')  # Add 'dragonfruit' to the end of the list
print(fruits)                 # ['apple', 'blueberry', 'cherry', 'dragonfruit']

Immutable Types

On the other hand, examples of immutable types are ints, floats, strings, and tuples. Once created, you can't change their content. Here's a string example:
message = 'hello'
print(message, type(message))  # hello <class 'str'>
If you try to change part of the string, Python won't allow it:
message[0] = 'H'  # Trying to change 'h' to 'H' will give you a TypeError
How come int is immutable? Can’t we change the value with += or -=?
When we talk about mutability in Python, we mean that the actual value stored in memory can be altered. For example, when we have a list, we can change an item in the list, and it's still the same list in memory.
Integers in Python are immutable. This means that the value in memory can't be changed. If we have an integer, a = 5, and we perform an operation like a += 2, it might look like we changed a. However, what Python actually did was create a new object in memory with a value of 7 and updated a to point to this new object. The original 5 wasn't changed in memory - which is why ints are considered immutable.
a = 5
print(id(a))  # This prints the memory location of a. Let's say it's 1001

a += 2
print(a)      # This prints 7
print(id(a))  # This will print a different memory location, let's say 1002
The id function in Python returns the identity of an object, which is unique and constant for the object during its lifetime. So, when we see that the identity of a changed after the operation a += 2, it's a clear sign that a now points to a new object in memory. The original integer 5 wasn't changed, confirming that integers are indeed immutable in Python.

Mutable Arguments in Functions

When we pass a mutable object (like a list) into a function, Python gives the function a reference to that object. This means that if the function modifies the mutable object, that modification will be seen outside of the function as well.
Let's use a function that receives a list and adds an element to it:
def add_item(my_list):
    my_list.append('added item')  # Append a new item to the list

shopping_list = ['apples', 'bananas', 'cherries']
print(shopping_list)  # Prints: ['apples', 'bananas', 'cherries']

# Now we use the function to add an item to our shopping list
print(shopping_list)  # Prints: ['apples', 'bananas', 'cherries', 'added item']
As you can see, when we printed shopping_list after calling the add_item function, the list had a new item. This happened even though we did not directly modify shopping_list outside the function.
However, it's essential to understand that if you assign a new value to the whole mutable object within the function, that won't affect the original object. This is because you're giving the function's local variable a new reference, not modifying the original object. Let's see an example of this:
def change_list(my_list):
    my_list = ['entirely', 'new', 'list']  # This won't affect the original list

shopping_list = ['apples', 'bananas', 'cherries']
print(shopping_list)  # Still prints: ['apples', 'bananas', 'cherries']
Even though we called the change_list function with shopping_list as an argument, the content of shopping_list did not change. That’s because inside the function, my_list was given a new reference (to an entirely new list), while the shopping_list reference remained untouched.
The key takeaway here is: when you pass a mutable object into a function in Python, remember that if the function changes the object (like adding an item to a list), the change is permanent and seen outside the function. But if you give a new value to the entire object inside the function, that won't affect the original object.
To check your solution you need to sign in
Sign in to continue