Introduction
In Python development, understanding the reference differences between mutable objects (such as lists and dictionaries) and immutable objects (such as integers and strings) is crucial. This not only affects code performance but can also lead to subtle bugs. As an experienced developer, I will share insights from practical development to help you master this core concept.
Overview of Python Object Model
All data in Python is an object, and each object has a unique identifier (id) and type. Objects are divided into two categories: mutable objects can have their values modified (e.g., appending elements to a list), while immutable objects cannot be changed once created (e.g., string concatenation creates a new object).
The Importance of Reference Differences
The differences in references manifest in scenarios such as object assignment and function parameter passing. Improper handling can lead to unintended data modifications or memory waste. For example, when passing a mutable object to a function, the original object may be inadvertently changed, causing logical errors.
1. Core Concepts of Reference Mechanism
1.1 The Essence of Variables: Labels, Not Containers
In Python, a variableis not a container that stores data, but rathera label pointing to an object:

1.2 Three Elements of Identity Verification
| Method | Function | Example |
|---|---|---|
<span>id()</span> |
Get the unique identifier of an object | <span>id(x)</span> returns the memory address |
<span>is</span> |
Compare object identities | <span>x is y</span> |
<span>sys.getrefcount()</span> |
Get reference count | <span>sys.getrefcount(obj)</span> |
2. Reference Counting Mechanism
2.1 Principles of Reference Counting
import sys
a= [1, 2] # Reference count=1
print(sys.getrefcount(a)) # 2 (temporarily increased)
b=a # Reference count+1→2
c= [a, 3] # Reference count+1→3
del b # Reference count-1→2
c.pop(0) # Reference count-1→1
a=None # Reference count-1→0 → object is reclaimed
2.2 Scenarios of Reference Counter Changes
| Operation Type | Reference Count Change | Example |
|---|---|---|
| New Object Assignment | +1 | <span>x = object()</span> |
| Reference Assignment | +1 | <span>y = x</span> |
| Adding to Container | +1 | <span>list.append(x)</span> |
| Exiting Scope | -1 | Function End |
| Explicit Deletion | -1 | <span>del x</span> |
| Reassignment | Old Object-1, New Object+1 | <span>x = 5 → x = 10</span> |
3. Differences in References Between Mutable and Immutable Objects
3.1 Differences in Assignment Operations
During assignment, Python passes object references (pointers). After assigning a mutable object, multiple variables point to the same memory:
list1= [1, 2]
list2=list1 # list2 and list1 reference the same object
list2.append(3) # Modification affects list1
print(list1) # Outputs [1, 2, 3]
Assignment of immutable objects is safe:
num1=10
num2=num1 # num2 references a new object (but with the same value)
num2+=5 # Creates a new object, num1 remains unchanged
print(num1) # Outputs 10
4. Function Parameter Passing Mechanism
4.1 Passing Rules: Copies of Object References
Function parameters are passed by reference. Modifications within the function when passing mutable objects will affect the original object; passing immutable objects is safe.
def modify(value, items):
value=20 # New int object created
items.append(100) # Modifies the original object
num=10 # Immutable object
arr= [1, 2] # Mutable object
modify(num, arr)
print(num) # 10 (unchanged)
print(arr) # [1, 2, 100] (modified)
4.2 Default Parameter Trap
def add_item(item, items=[]):
items.append(item)
return items
print(add_item(1)) # [1]
print(add_item(2)) # [1, 2] (shared default list)
# Correct implementation
def safe_add(item, items=None):
items= [] if items is None else items
items.append(item)
return items
5. Advanced Reference Patterns
5.1 Shallow Copy vs Deep Copy
| Type | Characteristics | Example |
|---|---|---|
| Assignment | Original reference | <span>b = a</span> |
| Shallow Copy | Copies the top-level object | <span>b = a.copy()</span> or <span>b = list(a)</span> |
| Deep Copy | Recursively copies all objects | <span>import copy; b = copy.deepcopy(a)</span> |
import copy
orig= [1, [2, 3]]
shallow=orig.copy()
deep=copy.deepcopy(orig)
orig[1].append(4)
print(shallow) # [1, [2, 3, 4]] (shared sublist)
print(deep) # [1, [2, 3]] (completely independent)
Best Practice: Use deep copy for nested mutable objects.
5.2 Circular Reference Issues
class Node:
def __init__(self, value):
self.value=value
self.next=None
# Create circular reference
a=Node(1)
b=Node(2)
a.next=b
b.next=a
# After deletion, reference count remains 1
del a
del b
5.3 Weak Reference Applications
import weakref
class Data:
def __del__(self):
print("Object reclaimed")
obj=Data()
ref=weakref.ref(obj) # Create weak reference
print(ref()) # <__main__.Data at 0x...>
del obj # Object reclaimed
print(ref()) # None
6. Practical Significance of Reference Mechanism
6.1 Memory Efficiency Improvement
# Reusing large objects
large_data= [0]*1000000
reference1=large_data # No data copy
reference2=large_data
# Same memory address
print(id(large_data) ==id(reference1)) # True
6.2 Data Protection Strategy
# Prevent unintended modifications
config= (8080, "production") # Tuple is immutable
backup=config # Safe copy of reference
# Create new object when modification is needed
updated_config= (9000, config[1])
7. Visual Summary of Reference Mechanism
Core Concept Map

Object-Variable Relationship Model
Memory Address: 0x1000 [Object A]
↗
[Variable X] →
↘
[Variable Y] →
Memory Address: 0x2000 [Object B]
↗
[Variable Z] →
8. Conclusion and Application Scenarios
Core Knowledge Map

Application Scenario Decision Table
| Need | Technical Choice |
|---|---|
| Efficient Data Sharing | Direct Reference |
| Avoid Unintended Modifications | Immutable Objects or Deep Copy |
| Automatic Resource Release | Context Managers |
| Cache Management | Weak Reference Containers |
| Circular Structures | <span>__slots__</span> + Explicit Release |
| Memory Leak Diagnosis | <span>tracemalloc</span> + <span>objgraph</span> |
Understanding Python’s reference mechanism is the cornerstone of writing efficient and robust code. By mastering object identity, reference counting, mutability differences, and parameter passing rules, developers can avoid common pitfalls (such as circular references and unintended modifications) and design memory-efficient applications. In scenarios requiring fine memory control, weak references and forced reclamation mechanisms provide additional management capabilities.