In Python programming, a dictionary is a very powerful data structure that allows us to associate key-value pairs and efficiently search and manipulate these data. When we try to store custom objects in a dictionary, we often encounter a crucial concept: In Python, object assignment is actually reference assignment, not a deep copy of the object itself. This means that when you put a custom object into a dictionary, the dictionary stores a reference to that object, rather than a brand new copy of the object.
Basic Example of Storing Custom Objects
Let’s consider a simple Person
class:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
# Create a Person object
p1 = Person("Alice", 30)
# Store the object in a dictionary
people_dict = {}
people_dict["alice"] = p1
In this example, the people_dict
dictionary now contains an item with a key "alice"
and its value is a reference to the Person
type object p1
. If we modify the properties of this object:
p1.age = 31
Then when accessing this object through the dictionary, we will find that its age has also been updated:
print(people_dict["alice"].age) # Output: 31
This is because the dictionary stores references to Person
objects rather than independent copies of them. It stores a reference to the same memory address.
Deep Copy vs. Shallow Copy
This referencing behavior can lead to unexpected results when dealing with nested data structures or custom objects. For example, if a custom object contains mutable attributes (such as lists or another custom object), directly storing such an object in a dictionary and modifying it will affect the object obtained through the dictionary.
class Address:
def __init__(self, street, city):
self.street = street
self.city = city
class Person:
def __init__(self, name, age, address):
self.name = name
self.age = age
self.address = address
address = Address("Main St.", "Springfield")
p1 = Person("Bob", 40, address)
people_dict["bob"] = p1
# Modify the original address object
address.city = "Shelbyville"
# The person in the dictionary's address also changed
print(people_dict["bob"].address.city) # Output: Shelbyville
Solution: Deep Copy
To avoid problems caused by shared state, we sometimes need to ensure that the dictionary stores a complete copy of the object, rather than a reference to it. Python provides the copy
module’s deepcopy
function to achieve this goal:
import copy
# Use deep copy to store objects
people_dict["bob_deepcopy"] = copy.deepcopy(p1)
# Now even if you modify the original address object, the deep copied object is not affected
address.city = "Capital City"
print(people_dict["bob"].address.city) # Output: Capital City
print(people_dict["bob_deepcopy"].address.city) # Output: Shelbyville
In summary, when using dictionaries to store custom objects in Python, be sure to pay attention to the fact that they default to storing object references. For situations where you need to maintain independent states, use deepcopy
to perform a deep copy to avoid unexpected data changes due to shared referencing.