Difference between Mutable and Immutable Data types in Python

Mutable Data Types

  • Definition: Objects of mutable data types can be changed after their creation. You can modify the object without creating a new one.

  • Examples: list, dict, set, bytearray

  • Behavior:

    • You can change, add, or remove elements in place.

    • These changes reflect directly in all references to the object.

Example:

1. List

  • Addition:

    • append(element): Adds an element to the end of the list.

    • insert(index, element): Inserts an element at a specified index.

    • extend(iterable): Adds all elements of an iterable (like another list) to the end of the list.

    my_list = [1, 2, 3]
    my_list.append(4)  # [1, 2, 3, 4]
    my_list.insert(1, 5)  # [1, 5, 2, 3, 4]
    my_list.extend([6, 7])  # [1, 5, 2, 3, 4, 6, 7]
  • Removal:

    • pop(index): Removes and returns the element at the specified index (default is the last element).

    • remove(element): Removes the first occurrence of the specified element.

    • clear(): Removes all elements from the list.

    my_list = [1, 2, 3, 4]
    my_list.pop()  # Removes 4 -> [1, 2, 3]
    my_list.remove(2)  # Removes 2 -> [1, 3]
    my_list.clear()  # [] (empty list)
  • Traversal: Use a for loop to iterate over the list.

      my_list = [1, 2, 3]
      for item in my_list:
          print(item)
    

2. Dictionary

  • Addition:

    • Add a key-value pair directly: dict[key] = value.

    • update(dict_or_iterable): Updates the dictionary with key-value pairs from another dictionary or iterable.

    my_dict = {"a": 1}
    my_dict["b"] = 2  # {"a": 1, "b": 2}
    my_dict.update({"c": 3})  # {"a": 1, "b": 2, "c": 3}
  • Removal:

    • pop(key): Removes and returns the value associated with the key.

    • popitem(): Removes and returns the last inserted key-value pair.

    • del dict[key]: Deletes a key-value pair.

    • clear(): Removes all key-value pairs.

    my_dict = {"a": 1, "b": 2}
    my_dict.pop("a")  # {"b": 2}
    my_dict.popitem()  # Removes the last pair -> {}
    del my_dict["b"]  # KeyError if key doesn't exist
  • Traversal: Use a for loop to iterate over:

    • Keys: for key in my_dict:

    • Values: for value in my_dict.values():

    • Key-Value pairs: for key, value in my_dict.items():

    my_dict = {"a": 1, "b": 2}
    for key, value in my_dict.items():
        print(f"{key}: {value}")

3. Set

  • Addition:

    • add(element): Adds a single element to the set.

    • update(iterable): Adds multiple elements from an iterable.

    my_set = {1, 2, 3}
    my_set.add(4)  # {1, 2, 3, 4}
    my_set.update([5, 6])  # {1, 2, 3, 4, 5, 6}
  • Removal:

    • remove(element): Removes the specified element (raises KeyError if not found).

    • discard(element): Removes the specified element (does nothing if not found).

    • pop(): Removes and returns an arbitrary element.

    • clear(): Removes all elements.

    my_set = {1, 2, 3}
    my_set.remove(2)  # {1, 3}
    my_set.discard(4)  # {1, 3} (no error even if 4 is not present)
    my_set.pop()  # Removes an arbitrary element
  • Traversal: Use a for loop to iterate over the set (note: order is not guaranteed).

      my_set = {1, 2, 3}
      for item in my_set:
          print(item)
    

Summary Table

Data TypeAdditionRemovalTraversal
Listappend, insert, extendpop, remove, clearfor item in list
Dictionarydict[key] = value, updatepop, popitem, clear, delfor key, value in dict.items()
Setadd, updateremove, discard, pop, clearfor item in set

Immutable Data Types

  • Definition: Objects of immutable data types cannot be changed after their creation. If you attempt to modify the object, a new object is created.

  • Examples: int, float, str, tuple, frozenset, bytes

  • Behavior:

    • Any "modification" creates a new object, leaving the original object unchanged.

    • Immutable types are hashable and can be used as keys in dictionaries.

Example:

1. Tuple

  • Addition:

    • Tuples are immutable, so you cannot add elements to an existing tuple.

    • Instead, you can create a new tuple by concatenation.

    t = (1, 2, 3)
    new_tuple = t + (4,)  # Creates a new tuple (1, 2, 3, 4)
  • Removal:

    • Not allowed. You must create a new tuple excluding the element.
    t = (1, 2, 3)
    new_tuple = tuple(x for x in t if x != 2)  # Creates a new tuple (1, 3)
  • Traversal:

    • Use a for loop to iterate over elements.
    t = (1, 2, 3)
    for item in t:
        print(item)
  • Hashable:

    • Hashable means the object has a unique, fixed hash value that remains constant during its lifetime.

    • Since tuples are immutable, they are hashable if all their elements are also hashable.

    • Example:

        t = (1, "hello")
        print(hash(t))  # Hash value (works since all elements are hashable)
      

2. String

  • Addition:

    • Strings are immutable, so addition creates a new string (concatenation).
    s = "hello"
    new_string = s + " world"  # Creates a new string "hello world"
  • Removal:

    • Not allowed directly. Create a new string excluding unwanted parts.
    s = "hello"
    new_string = s.replace("e", "")  # Removes "e", creating "hllo"
  • Traversal:

    • Use a for loop to iterate over characters.
    s = "hello"
    for char in s:
        print(char)
  • Hashable:

    • Strings are hashable because they are immutable.

    • Example:

        s = "hello"
        print(hash(s))  # Outputs the hash value of the string
      

3. Integer

  • Addition:

    • Integers are immutable. Any addition operation creates a new integer.
    x = 5
    y = x + 1  # Creates a new integer 6
  • Removal:

    • Not applicable for integers.
  • Traversal:

    • Not applicable because integers are single values, not collections.
  • Hashable:

    • Integers are hashable as they are immutable.

    • Example:

        x = 42
        print(hash(x))  # Outputs the hash value of the integer
      

4. Float

  • Addition:

    • Floats are immutable. Any arithmetic creates a new float object.
    x = 3.14
    y = x + 2.0  # Creates a new float 5.14
  • Removal:

    • Not applicable for floats.
  • Traversal:

    • Not applicable because floats are single values, not collections.
  • Hashable:

    • Floats are hashable as they are immutable.

    • Example:

        x = 3.14
        print(hash(x))  # Outputs the hash value of the float
      

What Does "Hashable" Mean?

  • Definition:

    • An object is hashable if it has a hash value (a number that represents the object) that remains constant during its lifetime.

    • Hashable objects can be used as keys in dictionaries or as elements in sets.

    • Mutability usually makes an object unhashable because changes would alter its hash value.

  • Rules for Hashability:

    • The object must be immutable.

    • It must implement the __hash__() method.

    • It must implement the __eq__() method to compare equality.

  • Hashable Examples:

    • Immutable types: int, float, str, tuple (if all elements are hashable).

    • Example:

        d = {("a", 1): "value"}  # Tuples used as keys
        print(d[("a", 1)])  # Outputs: "value"
      
  • Unhashable Examples:

    • Mutable types like list, set, and dict are not hashable.

    • Example:

        l = [1, 2, 3]
        # print(hash(l))  # TypeError: unhashable type: 'list'
      

Summary Table for Immutable Types

Data TypeAdditionRemovalTraversalHashable?
TupleConcatenation (+)Create a new tuplefor item in tupleYes (if elements are hashable)
StringConcatenation (+)Create a new stringfor char in stringYes
IntegerCreate a new integerNot applicableNot applicableYes
FloatCreate a new floatNot applicableNot applicableYes

When to use what

  • Use list if you need a dynamic collection and order matters.

  • Use tuple if the data is constant and should not be modified.

  • Use dictionary for key-value associations with efficient lookups.

  • Use set for unique elements or when performing set operations.