The Power of Immutability in Python: Beyond Tuples
Ah, immutability - that magical property where an object says "what I am is what I'll always be." Let's dive deeper into this concept and explore various ways to leverage immutability in Python, starting with our excellent example of grouping anagrams.
Img source: python-post
The Anagram Grouping Problem: A Case Study in Immutability
As we've seen in LC-49, grouping anagrams requires us to identify strings that are rearrangements of the same letters. Our approach uses dictionaries cleverly, but we hit a snag:
# Our first attempt - creating character counts
from collections import Counter
kstrs = [Counter(s) for s in strs]
# Trying to use these as dictionary keys
kmap = defaultdict(list)
for k, s in zip(kstrs, strs):
kmap[k].append(s) # ERROR: TypeError: unhashable type: 'Counter'
Why Did This Fail?
When we try to use a Counter (or any dictionary/list) as a key, Python throws that "unhashable type" error. This isn't Python being difficult - it's Python protecting the integrity of dictionaries!
Dictionary keys must be immutable because Python uses hashing to locate keys quickly. If a key could change after being added to a dictionary, its hash would change too, and the dictionary would never find it again.
It's like if someone kept changing their home address while you're trying to deliver their mail! 😱
The Solution: Tuple to the Rescue!
Tuples are immutable ordered collections, making them perfect for our needs:
kmap = defaultdict(list)
for k, s in zip(kstrs, strs):
# Convert mutable structures to an immutable tuple
immutable_key = tuple(sorted(k.items()))
kmap[immutable_key].append(s)
return [x for _, x in kmap.items()]
# Output: [['eat', 'tea', 'ate'], ['tan', 'nat'], ['bat']]
Lets break it down
`k.items()` : it will create a list of tuples, [ (a,1), (b,2)] but list is mutable
`sorted(…)`: Since in dictionary keys are unordered, we need to sort them
`tuple(…)` : Finally, to make our unique keys immutable to be used as key in dictionary, we called `tuple`
More Ways to Create Immutability in Python
Beyond our anagram example, let's explore other techniques for creating immutable objects:
1. Built-in Immutable Types
Python provides several immutable types out of the box:
# Integers, floats, complex numbers
x = 42 # Immutable
y = 3.14 # Immutable
# Strings
name = "Python" # Immutable
# Tuples (as we've seen)
coordinates = (10, 20) # Immutable
# Frozen sets
unique_immutable_items = frozenset([1, 2, 3]) # Immutable set
2. Named Tuples for Readable Immutable Records
from collections import namedtuple
# Creating a named tuple type
Person = namedtuple('Person', ['name', 'age', 'occupation'])
# Creating an instance
alice = Person(name='Alice', age=30, occupation='Engineer')
# Access by name instead of index
print(alice.name) # 'Alice'
# Still immutable!
# alice.age = 31 # This will raise AttributeError
Named tuples give you the best of both worlds: the immutability of tuples with the readability of accessing attributes by name!
3. Dataclasses with Frozen Option
Python 3.7+ introduced dataclasses with a frozen parameter:
from dataclasses import dataclass
@dataclass(frozen=True)
class Point:
x: float
y: float
p = Point(1.0, 2.0)
# p.x = 3.0 # Will raise FrozenInstanceError
4. Custom Immutable Classes
You can create your own immutable classes by overriding __setattr__ and __delattr__:
class ImmutablePerson:
def __init__(self, name, age):
object.__setattr__(self, 'name', name)
object.__setattr__(self, 'age', age)
def __setattr__(self, name, value):
raise AttributeError("Cannot modify immutable instance")
def __delattr__(self, name):
raise AttributeError("Cannot delete attribute from immutable instance")
5. Using Property Decorators with Private Attributes
class Person:
def __init__(self, name):
self._name = name
@property
def name(self):
return self._name
# No setter means name is read-only
Use Cases for Immutability
Dictionary/Set Keys: As we've seen in our anagram example
Thread Safety: Immutable objects can't be changed by concurrent threads
Caching: Immutable objects make great cache keys
Defensive Programming: Prevent accidental modification of important data
Functional Programming: Enables pure functions without side effects
Configuration: Immutable config objects prevent runtime tampering
Summary Table: Immutability Options in Python
Need to know more?
Conclusion: Why Immutability Matters
Immutability isn't just a quirky feature of certain data types - it's a powerful concept that can make your code more predictable, safer, and often more efficient. By understanding when and how to use immutable objects, you can avoid entire classes of bugs and design more robust systems.
So next time you're in a technical interview and someone asks about the difference between lists and tuples, don't just say "tuples are immutable" - explain why that matters and how you've used immutability to solve real problems. Your future employer (and your code) will thank you! 💰
And remember, in a world of constant change, sometimes the most valuable things are those that stay the same. Like tuples. And diamonds. But tuples are cheaper. 💎
P.S. If someone asks you to modify a tuple, just tell them: "I would, but I'm afraid it's against my immutable principles." 😉




