Keywords: Python | Dictionary | List | Reference | Copy
Abstract: This article provides an in-depth analysis of the reference behavior observed when appending dictionaries to lists in Python. It systematically explains core concepts including mutable objects and reference mechanisms, and introduces shallow and deep copy solutions with comprehensive code examples and memory model analysis to help developers thoroughly understand and avoid this common pitfall.
Problem Phenomenon and Background
In Python programming, developers often encounter a puzzling phenomenon: after appending a dictionary object to a list, modifying the original dictionary unexpectedly affects the dictionary values already stored in the list. The root cause of this behavior lies in Python's object reference mechanism, and understanding this mechanism is crucial for writing robust code.
Core Principles of Reference Mechanism
All variables in Python are references to objects, not the objects themselves. When executing a.append(b), what actually gets added to list a is the reference (i.e., memory address) of the dictionary object that variable b refers to. This means both the element in the list and variable b point to the same dictionary object.
To intuitively understand this process, consider the following memory model:
# Initial state
b = {1: 'one'} # b references dictionary object D
After executing a.append(b):
# State after appending
List a[0] and variable b both reference the same dictionary object D
Therefore, when modifying the dictionary via b[1] = 'ONE', you're actually modifying the shared dictionary object D, causing the corresponding value in list a to change as well.
Solutions: Shallow Copy and Deep Copy
To avoid this unintended reference sharing, you need to create a copy of the dictionary rather than using the original reference directly. Python provides two main copying approaches:
Shallow Copy Solution
Using the dictionary's copy() method creates a shallow copy of the original dictionary:
a = []
b = {1: 'one'}
a.append(b.copy()) # Create shallow copy of b and append
# Verify independence
b[1] = 'ONE'
print(a) # Output: [{1: 'one'}]
print(b) # Output: {1: 'ONE'}
Shallow copy creates a new dictionary object containing references to all key-value pairs from the original dictionary. For immutable values (like strings, numbers), this copy is completely independent; however, for nested mutable objects, reference sharing still exists.
Deep Copy Solution
When the dictionary contains nested mutable objects, use the copy.deepcopy() function:
import copy
# Example with nested dictionary
b = {1: 'one', 'nested': {'key': 'value'}}
a = []
a.append(copy.deepcopy(b)) # Create deep copy
# Modify original dictionary and its nested content
b[1] = 'ONE'
b['nested']['key'] = 'modified'
print(a) # Output: [{1: 'one', 'nested': {'key': 'value'}}]
print(b) # Output: {1: 'ONE', 'nested': {'key': 'modified'}}
Deep copy recursively copies all nested objects, ensuring complete independence from the original object.
Alternative Approach Analysis
Besides the above methods, you can also use the dict() constructor to create dictionary copies:
a = []
b = {1: 'one'}
a.append(dict(b)) # Use dict constructor to create copy
b[1] = 'iuqsdgf'
print(a) # Output: [{1: 'one'}]
This approach essentially performs a shallow copy and is suitable for simple dictionary structures. Compared to the copy() method, the dict() constructor provides clearer intent expression.
Application Scenarios and Best Practices
In practical development, choosing the appropriate copying strategy requires considering the following factors:
Performance Considerations: Shallow copy is more efficient than deep copy, especially when dealing with large data structures. If you're certain the dictionary contains no nested mutable objects, prefer shallow copy.
Data Integrity: When dictionaries contain complex nested structures, deep copy is the only reliable method to ensure data integrity. This is particularly important in scenarios like configuration management and state preservation.
Code Readability: Explicitly using copy() or deepcopy() enhances code maintainability, allowing other developers to clearly understand the copying depth and intent.
Conclusion
Python's reference mechanism is a core component of its dynamic nature. Understanding the distinction between object references and copies is essential for avoiding unintended side effects. By appropriately applying shallow and deep copy techniques, developers can precisely control dependencies between objects and write more reliable and maintainable Python code. In real-world projects, it's recommended to choose the most suitable copying strategy based on specific data structures and business requirements.