Summarise With AI
Back

Difference Between Mutable and Immutable in Python

19 Feb 2026
5 min read

Key Highlights of the Blog

  • Using concise memory-level examples and real-world code problems, this tutorial explains mutable versus immutable objects in Python.
  • To demonstrate how data is shared and altered, object identification, references, and assignments are discussed.
  • a thorough analysis of the behavior and applications of lists, dictionaries, strings, tuples, and integers.
  • Analysis of how mutable and immutable objects behave inside functions and affect the original data.
  • Coverage of common mistakes, copying techniques, and best practices to write safe and reliable programs.

Introduction

Many Python learners feel confident while writing basic programs, but start facing confusion when variables behave unexpectedly. A small change in one part of the program may affect another part without any warning. This usually happens due to a misunderstanding of the difference between mutable and Immutable in Python.

In professional software development, even small memory-related mistakes can cause serious failures. In technical interviews, this topic is often used to test how deeply a candidate understands Python fundamentals.

Learning this concept early helps you:

  • Write reliable programs
  • Avoid logical errors
  • Improve coding confidence
  • Perform better in interviews

This blog explains everything step by step using simple language and practical examples.

Definition of Mutable and Immutable Objects in Python

In Python, everything is an object, whether it’s a number, a string, a list, or a user-defined type. Each object in memory has three key properties:

  1. Object identity: The unique memory address where the object is stored. You can view this address using the id() function.
  2. Object type: The kind of object (e.g., int, str, list). This determines what operations the object supports.
  3. Object value: The actual data or internal state held by the object.

What Are Mutable Objects?

Mutable objects are objects whose internal state (value) can be changed after they are created, without changing their identity or memory address. This means you can modify the object “in place.”

Common mutable types in Python:

  • Lists: Ordered collections that can be changed by adding, removing, or updating elements.
  • Dictionaries: Collections of key-value pairs. You can add, update, or remove entries.
  • Sets: Unordered collections of unique elements. Elements can be added or removed.
  • User-defined objects: Most custom classes are mutable by default.

Example: Modifying a List In Place

numbers = [1, 2, 3]

print(id(numbers))      # e.g., 140012345678912

numbers.append(4)

print(numbers)          # [1, 2, 3, 4]

print(id(numbers))      # Same as before

Here, the list’s memory address (identity) doesn’t change, even after modification.

Implications of Mutability:

  • In-place modification: You can change the object directly, which is efficient for large data structures.
  • Shared references: If multiple variables reference the same mutable object, a change through one variable affects all others.
  • Thread safety: Mutable objects are not inherently thread-safe; simultaneous modifications from different threads can cause issues.

What Are Immutable Objects?

Immutable objects are objects whose value cannot be changed after creation. Any operation that seems to modify the object will instead create a new object with a different memory address, leaving the original unchanged.

Common immutable types in Python:

  • Strings: Data in text format. Every change results in a new string. 
  • Tuples: Ordered collections. The tuple itself cannot be changed, though it can contain mutable items.
  • Numbers: int, float, bool—their values cannot be changed after creation.
  • Frozensets: Immutable version of a set.

Example: Modifying a String

greeting = "Hello"

print(id(greeting))      # e.g., 140012345679888

greeting = greeting + " world"

print(greeting)          # "Hello world"

print(id(greeting))      # Different address

Here, concatenating to the string creates a new object with a new identity.

Implications of Immutability:

  • Safety: Immutable objects are safe from accidental changes, making your code more predictable.
  • Dictionary keys: Only immutable objects can be used as dictionary keys, because their value must not change.
  • Thread safety: Immutable objects are naturally thread-safe since they cannot be changed.
  • No in-place modification: Any “change” creates a new object.

Object Identity, Type, and Value

  • Object identity (memory address) remains the same for mutable objects after modification, but changes for immutable objects if you try to modify them.
  • Object type never changes after creation.
  • Object value can change for mutable objects, but not for immutable ones.

Practical Scenarios

1. Using Mutable Objects

  • Lists are ideal when you need to collect and update data, such as a shopping cart or a dynamic collection of results.
  • Sets are useful for collections when uniqueness and fast membership checks are important.
  • Lookup tables and configuration data are examples of values that are mapped to keys using dictionaries. 

2. Using Immutable Objects

  • Text should not change abruptly because it is kept in strings.
  • Tuples are useful for fixed collections, such as coordinates or RGB color data.
  • Computations cannot unintentionally modify the original value since numbers are always immutable. 

3. Dictionary Keys

Exclusively immutable types, such as strings, integers, or tuples with exclusively immutable components, may be used as dictionary keys. This is because if a key could change, it would break the internal structure of the dictionary.

my_dict = {}

my_dict["key"] = "value"      # OK

my_dict[[1, 2, 3]] = "value"  # Error: lists are mutable and unhashable

4. Shared References and Side Effects

Two variables that have a mutable object assigned to them both make reference to the same object. Modifying one variable has an impact on the other:

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

With immutable objects, this does not happen:

x = 10 
y = x 
y = 20 
print(x) # 10

In summary:

Understanding the difference between mutable and immutable in Python is vital for writing reliable, efficient, and bug-free Python code. It affects how your data behaves, how memory is managed, how objects are shared, and how your programs perform—especially as they grow in complexity or scale.

List of Mutable and Immutable Data Types in Python

Knowing which data types are mutable or immutable helps you choose the right structure.

Immutable Data Types

Type Description Use Case
int Whole numbers IDs, counters
float Decimal numbers Calculations
str Text Messages
tuple Fixed collections Coordinates
bool True / False values Conditions
frozenset Fixed (immutable) set Unique constants

These objects never change once created.

Example:

x = 10 
x = 20

A new object is created for 20.

Mutable Data Types

Type Description Use Case
list Ordered collection Student records
dict Key-value pairs User profiles
set Unique items Tag systems
bytearray Binary data Files

These objects allow in-place modification.

Example:

data = {"name": "Amit"} 
data["age"] = 20

Dictionary updated directly.

Important Case: Tuples with Mutable Items

Tuples are immutable, but can contain mutable elements.

Example:

t = (1, [2,3]) 
t[1].append(4)

This works because the list inside is mutable. This is a common interview trick question.

Difference Between Mutable and Immutable in Python

The difference between mutable and immutable in Python lies in whether an object can be modified after it is created. Understanding mutable and immutable data types in Python helps you manage memory efficiently, avoid unintended side effects, and write safer, more predictable programs.

Feature Mutable Objects in Python Immutable Objects in Python
Definition Objects whose values can be changed after creation. Objects whose values cannot be changed after creation.
Modification Can be modified directly in the same memory location. Cannot be modified; requires creation of a new object.
Memory Address Remains the same after modification. Changes when value is modified.
Behavior in Functions Can be changed inside functions and affect original data. Cannot be changed inside functions.
Safety More likely to cause accidental changes. Safer because values cannot be altered.
Performance Better for frequent updates. May be slower for frequent modifications.
Use as Dictionary Keys Cannot be used (not hashable). Can be used (hashable).
Common Examples Lists, dictionaries, sets. Strings, numbers, tuples.

Memory Behavior of Mutable and Immutable Objects

Python uses reference-based memory management. Variables point to objects. Understanding this avoids confusion. Let’s look at the memory behavioral difference between Mutable and Immutable in Python.

Memory with Immutable Objects

Example:

a = 5 
b = a
a = 10

Explanation:

  • a and b initially point to 5
  • a is reassigned
  • b still points to 5

No shared modification happens.

This ensures safety.

Memory with Mutable Objects

Example:

x = [1,2] 
y = x 
x.append(3)

Both x and y point to same list.

Change in one affects both.

This can be useful or dangerous depending on use.

Using id() Function

print(id(x), id(y))

Same id means same object.

This helps during debugging.

Why Python Uses Both Mutable and Immutable Objects

Python is intended for both novices and experts. There would be less flexibility if only one kind was used.

Why Immutability Is Important

  1. Prevents accidental changes
  2. Makes debugging easier
  3. Improves thread safety
  4. Enables dictionary keys
  5. Helps caching

Example: Passwords, tokens, IDs should be immutable.

Why Mutability Is Important

  1. Improves speed
  2. Saves memory
  3. Supports dynamic programs
  4. Makes coding easier

Example: Shopping cart, student lists, game scores.

Balanced Design

Python uses both to support different programming needs.

Behavior and Characteristics of Mutability

Understanding how mutable and immutable objects behave during various operations is essential for writing robust Python code. The interaction between object mutability and modification, copying, assignment, and function calls is examined in this section, along with the consequences for thread safety, performance, and possible adverse effects.

Object Identity, Type, and References

  • Object identity: Each object in Python has a unique identity (memory address), accessible via id().
  • Object type: What actions are permitted depends on the type (such as list or str).
  • References: Variables store references to objects, not the objects themselves.

Mutation and Mutator Methods

  • Mutation: Changing the internal state of an object.
    • Mutator functions (such as append(), delete(), and update()) enable in-place modifications for mutable collections (such as lists or dictionaries).
    • Immutable objects (like strings or tuples) do not support mutation; any “change” creates a new object.

Example:

a = [1, 2, 3]
a.append(4)          # Mutates the list in place

b = "hi"
b = b + " there"     # Creates a new string object

Assignment and Aliasing

  • Assignment: Assigning one variable to another (b = a) causes both variables to refer to the same thing.
  • Aliases: Several names that refer to the same thing. Any alias that is used to change the object has an impact on all references. 

Example:

x = [10, 20]

y = x   # y is an alias for x

y.append(30)

print(x)   # [10, 20, 30] (x and y are the same object)

Copying: Shallow vs Deep Copy

  • Shallow copy: Copies the outer object, but references to inner objects remain shared. Use .copy() or list().
  • Deep copy: Recursively copies all nested objects. Use copy.deepcopy().

Example:

import copy

original = [[1, 2], [3, 4]]

shallow = original.copy()
deep = copy.deepcopy(original)

shallow[0].append(99)

print(original)   # [[1, 2, 99], [3, 4]]
print(deep)       # [[1, 2], [3, 4]]

Augmented Assignment Operators

  • Operators like +=, *=, or |= may behave differently for mutable vs immutable objects.
    • For mutable objects (e.g., lists), += mutates the object in place.
    • For immutable objects (e.g., strings, tuples), += creates a new object.

Example:

lst = [1, 2]

lst += [3]   # Mutates in place

s = "abc"

s += "d"     # Creates a new string object

Function Arguments and Unintended Side Effects

  • You are sending references to functions when you provide objects to them.
    • If a function mutates a mutable argument, the change is visible outside the function (unintended side effect).
    • Immutable arguments cannot be changed inside the function.

Example:

def add_item(lst):
    lst.append(100)

nums = [1, 2]

add_item(nums)

print(nums)   # [1, 2, 100]

Performance Impact

  • Large data may be modified in-place with mutable objects, which saves memory and performance.
  • It may be necessary to make fresh copies of immutable objects for repeated alterations, which can take longer (e.g., string concatenation in a loop).

Thread Safety

  • Mutable objects: Data corruption may result from concurrent updates; they are not intrinsically thread-safe.
  • Immutable objects: Since immutable objects cannot be altered, sharing them between threads is safe.

Practical Applications and Use Cases

Making the decision between mutable and immutable objects is essential to well-written Python code. Depending on your objectives for code clarity, performance, and data integrity, each kind provides unique benefits.

When to Use Mutable Objects

Best for dynamic data scenarios and in-place modifications:

  • Object manipulation: When you need to add, alter, or remove items on a regular basis, such as when revising a list of user activities, you are engaging in object manipulation.
  • Large data structures: When working with enormous datasets, efficiency is critical, and in-place modifications—such as adding to a list or updating a dictionary—are quick and memory-efficient.
  • Object-oriented programming: Mutable objects (like class instances) allow you to change object state, which is essential for modeling entities that evolve over time.
  • Parallel processing: Although mutable shared data structures are occasionally required in concurrent applications, they must be carefully managed (for example, via locks) to prevent data corruption.

Example:

cache = {}

def expensive_computation(x):
    if x in cache:
        return cache[x]

    result = x * x   # Placeholder for real computation
    cache[x] = result

    return result

To improve efficiency, function calls are cached in this case using a mutable dictionary.

When to Use Immutable Objects

Best for data integrity, thread safety, and stable baselines:

  • Data integrity: Immutable objects make your code more dependable and simpler to debug by preventing unintentional modifications.
  • Functional programming: A fundamental tenet of functional programming is immutability, which guarantees that functions don't have unintended consequences and simplifies code reasoning.
  • Caching and hash keys: Only immutable objects can be used as dictionary keys or set elements, which is essential for fast lookups and caching.
  • Stable and unchanging baseline: For configuration, constants, or other data that should not change throughout the course of the application, use immutable objects (such as strings or tuples).
  • Parallel processing: Immutable objects are inherently thread-safe, as their state cannot be altered by concurrent threads.
  • Memory management: While immutability can require new objects to be created, it also allows for safe sharing of data without defensive copying.

Example:

config = ("API_URL", "v1", "production")   # Tuple as immutable configuration

user_ids = {"alice", "bob", "carol"}       # Set for fast membership checks

Advantages and Disadvantages

Mutable objects: As they enable effective updates, are perfect for managing dynamic data, and provide flexibility in altering their contents, mutable objects are beneficial. However, they come with the risk of unintended side effects and are generally less thread-safe, meaning concurrent modifications can lead to bugs.

Immutable objects, on the other hand, are safer to use since their state cannot change after creation. They are thread-safe and can be used as a dictionary or set keys, making them predictable and reliable for fixed data. The main disadvantage is that they are less efficient when frequent changes are needed, as each modification results in the creation of a new object.

Summary

  • For collections and data that will change often, use mutable objects to facilitate in-place updates and effective memory management.
  • For fixed data, keys, and situations where thread safety, predictability, and safety are important considerations, use immutable objects.
  • Your particular use case will determine the best option, which will balance data integrity, performance, and flexibility.

Common Pitfalls and Best Practices

Understanding mutability in Python helps prevent subtle bugs and errors. Here are some frequent issues (gotchas) and best practices to keep your code safe and predictable:

1. Mutable Default Argument Values

Pitfall:

Using a mutable object (like a list or dictionary) as a default argument value in a function.

Why it’s a Python gotcha:

The default value is evaluated only once—when the function is defined, not each time it’s called. This means the same instance is reused every time, leading to unintended side effects and data consistency problems.

Example:

def add_item(item, my_list=[]):
    my_list.append(item)
    return my_list

print(add_item(1))   # [1]
print(add_item(2))   # [1, 2]

# Bug: list keeps growing!

Best Practice:

If necessary, create a new object inside the function and set None as the default value.

def add_item(item, my_list=None):
    if my_list is None:   # None as default value
        my_list = []

    my_list.append(item)
    return my_list

2. Aliasing and Unintended Side Effects

Pitfall:

Aliases, or several names for the same object, are created when one changeable object is assigned to many variables. Any alias that modifies the object has an impact on all references, which may result in mistakes and defects.

Example:

a = [1, 2, 3] 
b = a 
b.append(4)
print(a) # [1, 2, 3, 4] # Both variables changed!

Best Practice:

If you want independent objects, use .copy() for a shallow copy or the copy module for a deep copy.

3. Mutating Arguments in Functions

Pitfall:

A potential danger is that functions that alter their parameters may result in unanticipated modifications outside of the function, particularly if the caller did not anticipate it.

Example:

def double_in_place(numbers):
    for i in range(len(numbers)):
        numbers[i] *= 2


nums = [1, 2, 3]

double_in_place(nums)

print(nums)   # [2, 4, 6]

Best Practice:

When a function returns a new object or modifies its parameters rather than changing the input, make sure to properly document the change. 

4. Controlling Mutability in Custom Classes

Pitfall:

If custom classes are allowed to change unwanted attributes without enough control, data consistency may be jeopardized.

Best Practice:

Using specific methods and attributes (such as __setattr__, @property, or dataclasses with frozen=True) to limit or manage mutability in user-defined classes is the best practice.

5. Bugs and Errors from Mutable Objects

  • Same instance every time: Reusing mutable objects can lead to hidden state and hard-to-find bugs.
  • Mutability-related gotchas: Always be aware of how and where your objects might be changed.

By following these best practices, you can avoid common mutability pitfalls and write more reliable, maintainable Python code.

Mutability in Custom Classes

In Python, user-defined classes are mutable by default. This means you can add, change, or remove attributes from instances and classes at any time, allowing for flexible and dynamic objects. However, there are times when you want to control or restrict mutability to protect data consistency or create read-only objects.

Attribute Mutation and Reassignment

  • Instance attributes are unique to each object and can be freely modified:
class User:
    pass


u = User()

u.name = "Alice"   # Dynamic attribute assignment
u.name = "Bob"     # Attribute reassignment

del u.name         # Attribute deletion
  • All instances have the same class attributes, which can be modified:
class Example:
    count = 0


Example.count = 1   # Attribute reassignment

Controlling Mutability

To prevent accidental changes, you can enforce immutability or restrict changes in several ways:

1. Custom __setattr__ Implementations

You can block or modify attribute assignment by overriding the specific function __setattr__.

class Immutable:
    def __init__(self, value):
        super().__setattr__('value', value)

    def __setattr__(self, name, value):
        raise AttributeError("Cannot modify attribute")

An AttributeError will now be raised if an attribute is changed.

2. Read-Only Properties

You can use the @property decorator to create read-only attributes.

class Circle:
    def __init__(self, radius):
        self._radius = radius

    @property
    def radius(self):
        return self._radius   # No setter, so radius is read-only

Trying to assign circle.radius = 5 will raise an AttributeError.

3. Descriptors

Advanced techniques for controlling attribute access are descriptors. You may manage the reading, writing, and removing of attributes by defining a descriptor class.

class ReadOnly:
    def __init__(self, value):
        self._value = value

    def __get__(self, instance, owner):
        return self._value

    def __set__(self, instance, value):
        raise AttributeError("Read-only attribute")


class MyClass:
    attr = ReadOnly(42)

4. Dataclasses with frozen=True

Python’s dataclasses module allows you to create classes with immutable fields by setting the frozen argument to True.

from dataclasses import dataclass


@dataclass(frozen=True)
class Config:
    host: str
    port: int


c = Config("localhost", 8080)

# c.host = "127.0.0.1"
# Raises dataclasses.FrozenInstanceError

Summary Table

Technique Prevents Mutation? Prevents Deletion? Example Use Case
Custom __setattr__ Yes Yes (with __delattr__) Fully immutable objects
Read-only properties Yes (per attribute) No Expose some data as read-only
Descriptors Yes (customizable) Yes (if coded so) Advanced, reusable controls
Dataclasses with frozen=True Yes Yes Immutable data containers

Key Points

  • User-defined classes are mutable unless you explicitly restrict them.
  • You can enforce immutability using special methods, properties, descriptors, or dataclasses.
  • Attempting forbidden changes will typically raise an AttributeError or FrozenInstanceError.

Special Cases and Exceptions

While Python’s rules for mutable and immutable objects are generally clear, there are several important exceptions and special cases that can surprise even experienced developers.

1. Mutability Inside Immutable Containers

Once created, immutable containers, such as tuples and frozensets, cannot have their structure altered. These inner objects can still be changed, though, if they include mutable objects.

Example:

t = (1, [2, 3])
t[1].append(4) # This works!
print(t) # Output: (1, [2, 3, 4])

In this case, t[1] cannot have a new value assigned to it because the tuple itself is immutable. However, the tuple's list is changeable and can be modified in real time.

Implication:

  • This can lead to unexpected side effects and bugs, especially when tuples are used as dictionary keys (which requires all contents to be immutable and hashable).

2. Frozenset

A frozenset is an immutable version of a set. Once created, you cannot add, remove, or modify its elements.

Example:

fs = frozenset([1, 2, 3]) 
fs.add(4) # Raises Attribute
Error: 'frozenset' object has no attribute 'add'
  • Attempting in-place changes on a frozenset results in a TypeError or AttributeError.

3. Bytearray

A bytearray is a mutable sequence of bytes. Unlike the immutable bytes type, a bytearray can be changed in place using indices or slices.

Example:

b = bytearray(b'hello')
b[0] = ord('H')
print(b) # Output: bytearray(b'Hello')
  • This makes bytearray useful for binary data that needs to be modified.

4. In-Place Changes and Indices

  • You cannot use indices to alter elements of immutable types (such as strings, tuples, and frozen sets). A TypeError is raised when this is attempted.

Example:

s = "hello"

s[0] = "H"   # Raises TypeError: 'str' object does not support item assignment
  • For mutable types (like lists, bytearrays), you can assign to elements using indices.

Example:

lst = [1, 2, 3]
lst[1] = 100 
print(lst) # Output: [1, 100, 3]

5. Truthiness and Mutability

  • If a container contains changeable objects that are altered in place, its truthiness—that is, whether it evaluates as True or False—may change.

Example:

x = ([],)

print(bool(x))   # True, tuple is not empty

x[0].append(1)

print(bool(x))   # Still True, but now the contents have changed
  • Exercise caution since, although the external structure is unchangeable, the internal state might not be.

Key Takeaways:

  • Immutable containers can still hold mutable objects, which may be changed in place.
  • frozenset is a fully immutable set; bytearray is a mutable sequence of bytes.
  • Attempting to modify immutable objects in place will raise a TypeError or AttributeError.
  • Always be cautious when using mutable objects inside immutable containers, as it can lead to subtle bugs or unexpected behavior.

Conclusion

Writing accurate and effective programs requires an understanding of the difference between mutable and immutable in Python. It assists you in managing the sharing, editing, and memory storage of data. Due to the ambiguity of this concept, many logical flaws, performance problems, and interview errors occur. You may develop code that is safer, cleaner, and more polished by learning when things change in place and when new objects are generated.

Points to Remember

  1. Variables in Python point to objects in memory, not to actual values
  2. Lists, dictionaries, and sets can be modified, but strings and numbers cannot
  3. Changes to mutable objects affect all references pointing to them
  4. A fresh copy is not produced when one variable is assigned to another.
  5. Hidden defects can be avoided by using copying and immutability appropriately.

Frequently Asked Questions

1. What is the difference between mutable and immutable in Python?

Mutable objects can be changed after creation, meaning their internal state can be modified (e.g., lists, dictionaries, sets). Once generated, immutable objects—such as strings, tuples, and numbers—cannot be altered.

2. Can a tuple contain mutable objects, and what happens if it does?

Yes, a tuple can contain mutable objects like lists. While the tuple itself cannot be changed, the mutable object inside it can be modified, which can lead to unexpected side effects.

3. Why are strings immutable in Python?

Strings are immutable for safety and efficiency. Any operation that appears to modify a string actually creates a new string object, helping prevent accidental changes and making string objects hashable for use as dictionary keys.

4. What problems can occur if you use a mutable object as a default argument in a function?

Because the same object is used in all function calls, using a mutable object as the default argument may result in unexpected behavior and unintentional data sharing.

5. Are lists mutable in Python, and what does that mean for variable assignment?

Yes, lists are mutable. When you assign one list variable to another, both variables point to the same list in memory. Any changes made through one variable will be reflected in the other.

Summarise With Ai
ChatGPT
Perplexity
Claude
Gemini
Gork
ChatGPT
Perplexity
Claude
Gemini
Gork
Chat with us
Chat with us
Talk to career expert