Introduction
In this post, I'll walk through key Python concepts I explored while learning more deeply about how objects, identity, and mutability work. We'll examine:
- Python's
id()
andtype()
functions - What makes objects mutable or immutable
- How this impacts program behavior
- Why it matters when passing arguments to functions
Throughout, I'll provide simple code examples to make each concept concrete and relatable for new and intermediate Python learners.
Understanding id()
and type()
Every object in Python has a unique identity that you can inspect using the id()
function. You can also inspect the object's class with the type()
function.
Basic Example
x = [1, 2, 3]
print(id(x)) # Outputs object's unique identifier (memory address)
print(type(x)) # <class 'list'>
Output:
140234567890123
<class 'list'>
Identity vs Equality
Even two variables holding identical contents may have different ids if they're different objects in memory. This helps distinguish identity (is
) from equality (==
):
a = [1, 2, 3]
b = [1, 2, 3]
print(a == b) # True, they have equal contents
print(a is b) # False, they're different objects
Key Takeaway: ==
checks if values are equal, while is
checks if they're the same object in memory.
Mutable Objects
A mutable object can have its internal state changed after it is created. Common examples include:
- Lists
- Dictionaries
- Sets
- Most classes with modifiable attributes
Example: Modifying a List
numbers = [1, 2, 3]
numbers.append(4)
print(numbers) # [1, 2, 3, 4]
Identity Persists After Modification
Changing the contents doesn't change the identity:
before = id(numbers)
numbers.append(5)
after = id(numbers)
print(before == after) # True
print(numbers) # [1, 2, 3, 4, 5]
Output:
True
[1, 2, 3, 4, 5]
Immutable Objects
Immutable objects cannot be changed in place after they're created. These include:
- Integers
- Floats
- Strings
- Tuples
- Frozensets
Any 'modification' creates a new object.
Example: Integers
x = 5
print(id(x))
x += 1 # This rebinds x to a new object
print(id(x)) # The id is different now
Output:
140734567823456
140734567823488 # Different id!
Example: Strings
s = "hello"
print(id(s))
s += " world"
print(id(s)) # New object, new id
print(s) # hello world
Why Does It Matter? Python's Treatment of Mutable vs Immutable Objects
Understanding mutability influences everything from performance to subtle bugs. Python stores small integers and short strings in a cache, so they may appear to share ids in some cases, but in general, identity and mutability are fundamental to how variables work.
Key Differences
Aspect | Mutable Objects | Immutable Objects |
---|---|---|
Can be changed in-place? | ✅ Yes | ❌ No |
Identity after modification | Same | Different |
Safe to share references? | ⚠️ Risky | ✅ Safe |
Examples | list, dict, set | int, str, tuple |
Implications
Mutable objects:
- Can be changed via various methods
- If two variables reference the same object, changes by one variable affect the other
Immutable objects:
- Safe to share
- Reassigning a variable to a new value doesn't alter the original object
Example Comparison
# Mutable example
def modify_list(lst):
lst.append(99)
l = [1, 2, 3]
modify_list(l)
print(l) # [1, 2, 3, 99], because lists are mutable
# Immutable example
def modify_int(n):
n += 1
x = 5
modify_int(x)
print(x) # 5, unchanged
Output:
[1, 2, 3, 99]
5
How Arguments Are Passed to Functions
Python's argument passing is often summarized as "assignment by object reference" or "pass by object reference."
What This Means
- For mutable types, functions can change the content of the argument outside the function
- For immutable types, any operation that looks like a modification (e.g.,
x += 1
) instead creates a new object and rebinds the local variable
More Examples
Mutable Example (List)
def add_element(mylist):
mylist.append('new')
lst = [0]
add_element(lst)
print(lst) # [0, 'new']
Output: [0, 'new']
✅ The original list was modified!
Immutable Example (Integer)
def add_one(num):
num += 1
print(f"Inside function: {num}")
n = 10
add_one(n)
print(f"Outside function: {n}")
Output:
Inside function: 11
Outside function: 10
❌ The original integer was NOT modified!
Why This Matters
This behavior is crucial to avoid unexpected side effects or bugs, especially when working with functions that handle lists or dictionaries.
Best Practices:
- 🔍 Be aware of which types are mutable
- 📝 Document if your function modifies arguments
- 🛡️ Use
.copy()
if you need to avoid modifying the original
Advanced Tasks: Exploring Object Internals
While digging deeper, I explored several advanced concepts:
Topics Covered
- ✅ Inspecting memory addresses of objects
- ✅ Testing Python's integer caching (small integers cached for efficiency)
- ✅ Careful copying:
list.copy()
vs slicing vs simple assignment
Copying Lists: Three Approaches
a = [1, 2, 3]
# Approach 1: Simple assignment (both refer to same object)
b = a
b.append(4)
print(f"a: {a}") # [1, 2, 3, 4] - a was affected!
# Approach 2: Using .copy() (creates separate object)
c = a.copy()
c.append(5)
print(f"a: {a}") # [1, 2, 3, 4] - unchanged
print(f"c: {c}") # [1, 2, 3, 4, 5]
# Approach 3: Using slicing (also creates separate object)
d = a[:]
d.append(6)
print(f"a: {a}") # [1, 2, 3, 4] - still unchanged
print(f"d: {d}") # [1, 2, 3, 4, 6]
Integer Caching Example
# Small integers are cached
x = 256
y = 256
print(x is y) # True - same object!
# Large integers are not
a = 1000
b = 1000
print(a is b) # False - different objects
Key Lessons
- Assignment doesn't copy - it creates another reference
-
Use
.copy()
or slicing to create independent copies - Python caches small integers (-5 to 256) for efficiency
- Understanding object identity helps debug confusing behavior
Conclusion
Understanding Python's object model, especially the difference between mutable and immutable objects, is foundational for writing reliable, bug-free code. It impacts:
- 🔧 Function arguments and side effects
- 🎯 Variable assignment behavior
- 📦 Copying data structures safely
- ⚡ Performance optimizations
Summary Table
Concept | Key Point |
---|---|
id() |
Returns unique object identifier |
type() |
Returns object's class/type |
Mutable | Can be modified in-place (list, dict, set) |
Immutable | Cannot be modified (int, str, tuple) |
Function Args | Passed by object reference |
Copying | Use .copy() or [:] for independent copies |
Practicing these patterns and observing object identities has fundamentally changed my perspective on how Python "really" works under the hood. I hope this guide helps you build a stronger mental model too! 🐍✨
Top comments (0)