Mutability in Python Explained (Mutable vs Immutable Types)
Mutability describes whether a Python object can change after it is created.
This matters because Python variables do not store independent copied values by default. They refer to objects. Once you understand that, many confusing results with lists, strings, and function arguments start to make sense.
In this guide, you will learn:
- What mutable and immutable mean
- Which common Python types are mutable or immutable
- How assignment works with objects
- Why functions can sometimes change your original data
- How to avoid common bugs
Quick example
x = [1, 2]
y = x
y.append(3)
print(x) # [1, 2, 3]
name = "Sam"
new_name = name.upper()
print(name) # Sam
print(new_name) # SAM
Lists can change in place. Strings cannot. Many beginner mistakes come from not knowing this difference.
What mutability means
A mutable object can be changed after it is created.
An immutable object cannot be changed after it is created.
That sounds simple, but it affects how Python behaves in important ways:
- If an object is mutable, code can change the same object in place
- If an object is immutable, any “change” actually creates a new object
- This matters most when more than one variable refers to the same object
- It helps explain many surprising results in beginner Python code
Here is a simple example:
numbers = [1, 2, 3]
numbers.append(4)
print(numbers)
Output:
[1, 2, 3, 4]
The list was changed in place.
Now compare that to a string:
text = "cat"
text = text + "s"
print(text)
Output:
cats
This looks like the string changed, but it did not. Python created a new string and then reassigned text to point to it.
Common immutable types
Common immutable Python types include:
strintfloatbooltuple
With immutable objects:
- You cannot change the object itself after creation
- Operations usually create a new object
- Reassigning a variable does not change the original object
Example with a string:
name = "Sam"
upper_name = name.upper()
print(name)
print(upper_name)
Output:
Sam
SAM
name.upper() does not change name. It returns a new string.
Example with an integer:
x = 10
x = x + 1
print(x)
Output:
11
Again, Python does not modify the original integer in place. It creates a new integer and assigns x to it.
If you want to learn more about string behavior, see Python strings explained.
Common mutable types
Common mutable Python types include:
listdictset
With mutable objects:
- The object can be changed after creation
- Many methods modify the object in place
- If two variables refer to the same object, both will reflect the change
Example with a list:
items = ["a", "b"]
items.append("c")
print(items)
Output:
['a', 'b', 'c']
Example with a dictionary:
user = {"name": "Ana"}
user["age"] = 25
print(user)
Output:
{'name': 'Ana', 'age': 25}
Example with a set:
colors = {"red", "blue"}
colors.add("green")
print(colors)
Output:
{'red', 'blue', 'green'}
These methods change the original object instead of returning a brand-new one.
For more on list behavior, see Python lists explained and Python dictionaries explained.
Variables store references, not boxes of copied values
A beginner-friendly way to think about this is:
- A variable name points to an object
- Using
=does not always make a new independent copy - Sometimes two variable names point to the same object
This is especially important with mutable types.
a = [1, 2]
b = a
b.append(3)
print(a)
print(b)
Output:
[1, 2, 3]
[1, 2, 3]
Why did a change when only b.append(3) was called?
Because a and b refer to the same list.
Now compare that with an immutable value:
x = "hi"
y = x
y = y.upper()
print(x)
print(y)
Output:
hi
HI
Here, x does not change because strings are immutable. y.upper() returns a new string, and y is updated to refer to that new object.
If you want a simple way to inspect object identity, Python has the id() function. It can help while debugging, but it is better to understand the behavior first instead of depending on id() alone.
Why mutability matters with functions
When you pass an object to a function, the function receives access to that same object.
That means:
- If the object is mutable, the function can change the original
- If the object is immutable, the function cannot change the original in place
Example with a list:
def add_item(my_list):
my_list.append("new")
data = ["old"]
add_item(data)
print(data)
Output:
['old', 'new']
The function changed the original list.
Now compare that with a string:
def make_upper(text):
text = text.upper()
print("Inside function:", text)
name = "Sam"
make_upper(name)
print("Outside function:", name)
Output:
Inside function: SAM
Outside function: Sam
The original string did not change.
This is one reason beginners get confused by Python functions explained. It can seem like arguments are copied automatically, but that is not what happens.
Mutability and copying
Assignment shares a reference. It does not usually make a copy.
original = [1, 2, 3]
alias = original
alias.append(4)
print(original)
Output:
[1, 2, 3, 4]
If you need an independent list, make a copy:
original = [1, 2, 3]
copied = original.copy()
copied.append(4)
print("original:", original)
print("copied:", copied)
Output:
original: [1, 2, 3]
copied: [1, 2, 3, 4]
A shallow copy creates a new outer object, but nested objects may still be shared.
a = [[1, 2], [3, 4]]
b = a.copy()
b[0].append(99)
print(a)
print(b)
Output:
[[1, 2, 99], [3, 4]]
[[1, 2, 99], [3, 4]]
a and b are different outer lists, but the inner lists are still shared.
For more on this, see Python shallow copy vs deep copy explained, how to copy a list in Python, and the Python list copy() method.
How to check and reason about object behavior
When you are unsure whether something changes in place, use this process:
- Look at the data type first
- Check whether the method changes the object or returns a new one
- Use
id()only as a helper - Read the method documentation when needed
Useful debugging commands:
print(type(value))
print(id(value))
print(value)
print(value is other_value)
help(list.append)
help(str.replace)
A practical example:
text = "hello"
new_text = text.replace("h", "H")
print(text)
print(new_text)
print(text is new_text)
Output:
hello
Hello
False
str.replace() returns a new string.
Now compare that to a list method:
numbers = [1, 2]
result = numbers.append(3)
print(numbers)
print(result)
Output:
[1, 2, 3]
None
list.append() changes the list in place and returns None.
That difference is very important when reading Python code.
Beginner rules to remember
These rules are not everything, but they will help you avoid many bugs:
- Strings and numbers usually create new objects
- Lists, dictionaries, and sets often change in place
- Be careful when reusing the same list or dictionary in multiple places
- Copy mutable data when you need an independent version
- Do not assume
a = bmakes a copy
Common mistakes
These are some very common beginner problems related to mutability:
- Assigning one list to another variable and expecting a separate copy
- Changing a list inside a function and being surprised the original changed
- Expecting string methods to modify the original string
- Using mutable default values in functions without understanding shared state
- Assuming all Python objects behave like lists
Example of a shared-list mistake:
a = [1, 2]
b = a
b.append(3)
print(a) # Unexpected for many beginners
Output:
[1, 2, 3]
Example of expecting a string to change in place:
name = "sam"
name.upper()
print(name)
Output:
sam
To keep the result, assign it:
name = "sam"
name = name.upper()
print(name)
Output:
SAM
FAQ
Are strings mutable in Python?
No. Strings are immutable. String operations return a new string.
Are lists mutable in Python?
Yes. Lists can be changed in place with methods like append(), insert(), remove(), and sort().
Does a = b copy a list?
No. It usually creates another reference to the same list.
Why did my function change my original list?
Because lists are mutable, and the function may have modified the same object you passed in.
How do I make an independent copy of a list?
Use list.copy(), slicing like my_list[:], or the copy module for more complex cases.