Learnitweb

Understanding Immutability in Python’s Fundamental Data Types

In this tutorial, we will explore a foundational and powerful concept in Python programming: immutability, especially with respect to fundamental data types. This topic is essential for understanding how Python manages memory, variable assignment, and object behavior.

Fundamental Data Types

Before diving into immutability, let’s first recall the fundamental (primitive) data types in Python:

  • int: Represents integers (e.g., 10, -5, 0)
  • float: Represents floating-point numbers (e.g., 3.14, -0.001)
  • complex: Represents complex numbers (e.g., 3 + 4j)
  • bool: Represents Boolean values (True or False)
  • str: Represents text (technically not always considered a primitive in other languages, but treated as fundamental in Python)

All of these types are immutable in Python.

What is Immutability?

In general terms:

  • Mutable means changeable. You can alter the contents of the object.
  • Immutable means not changeable. Once an object is created, its content cannot be altered.

In Python:
An immutable object is one whose value cannot be changed after it is created. If you try to change its value, Python creates a new object instead.

Immutability in Action: Understanding Through Examples

Let’s understand how immutability works with int, one of Python’s fundamental data types.

Example 1: Single Variable Assignment and Update

x = 10
print("ID of x:", id(x))

x = x + 1
print("ID of x after increment:", id(x))

Output:
ID of x: 140731423206104
ID of x after increment: 140731423206136

Explanation:

  • Initially, x refers to the integer object 10.
  • When we do x = x + 1, Python does not update the existing object. Instead:
    • It creates a new object with the value 11.
    • The variable x now refers to the new object.
    • The original object 10 is now unreferenced and becomes eligible for garbage collection.
  • You will see that the id() (which returns the memory address of an object) changes after reassignment.

Example 2: Two Variables Referencing the Same Object

x = 10
y = x
print("ID of x:", id(x))
print("ID of y:", id(y))

y = y + 1
print("After incrementing y:")
print("x =", x, "ID:", id(x))
print("y =", y, "ID:", id(y))

Explanation:

  • Initially, both x and y refer to the same object 10.
  • When we increment y, Python creates a new object with value 11.
  • y now refers to 11, while x continues to point to 10.

So:

  • Two objects are now in memory: one with value 10 and another with value 11.
  • The IDs of x and y are different after the update.

No object is garbage collected here because both 10 and 11 are still being referenced.

Key Takeaways

  • All fundamental data types in Python are immutable.
    This includes int, float, complex, bool, and str.
  • Once created, the value of an immutable object cannot be changed.
    Instead, any “modification” results in the creation of a new object.
  • Memory addresses (IDs) help prove immutability.
    You can use the id() function to see whether a new object is created.
  • Garbage Collection
    If an object is no longer referenced by any variable, Python’s garbage collector will clean it up.

Why is Immutability Required in Python?

Python uses immutability to support efficient memory usage and performance through object reusability.

Example: Reusing Integer Objects

a = 10
b = 10
c = 10
print(id(a), id(b), id(c))

Output:
140731113351896 140731113351896 140731113351896
  • Although these assignments appear on separate lines, Python does not create three separate objects.
  • Python checks if an object with the required value already exists.
  • If it does, Python reuses that object.

This means a, b, and c all point to the same object in memory.

Why is this beneficial?

  1. Memory Efficiency: Instead of creating multiple copies of the same value, one object is shared across multiple variables.
  2. Performance Improvement: Creating an object is expensive. By reusing objects, Python avoids repeated creation, which boosts performance.

Standard vs Non-Standard Behavior

When testing object identity (is) with Python interactive tools like IDLE or console, you might notice some unexpected behavior:

a = 1000
b = 1000
print(a is b)  # May return False in some REPLs

This happens because:

  • Python often reuses integers in the range [-5, 256].
  • Outside this range, behavior depends on the implementation (standard interpreter vs REPL).

To avoid confusion:

  • Use standard environments like PyCharm, VSCode, or standalone scripts.
  • Do not rely on REPLs (IDLE, console) for verifying object reuse behavior.

What is REPL?

REPL stands for:

  • Read
  • Eval
  • Print
  • Loop

These tools are meant for quick testing, not full-scale development.

Example: List Modification

l = [10, 20, 30]
print("Before modification:", l, "ID:", id(l))

l[0] = 7777
print("After modification:", l, "ID:", id(l))

Output:
Before modification: [10, 20, 30] ID: 2265743036736
After modification: [7777, 20, 30] ID: 2265743036736