在 Python 编程中,字典是一种非常强大的数据结构,它允许我们将键值对关联起来,并以高效的方式查找和操作这些数据。当我们尝试在字典中存储自定义对象时,通常会遇到一个关键概念:Python 中的对象赋值实际上是引用赋值,而非对象本身的深拷贝。这意味着将自定义对象放入字典时,字典中存储的是对该对象的引用,而非对象的一个全新副本。
存储自定义对象的基本示例
假设我们有一个简单的 Person
类:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
# 创建一个 Person 对象
p1 = Person("Alice", 30)
# 将对象存储到字典中
people_dict = {}
people_dict["alice"] = p1
在这个例子中,people_dict
字典现在包含一个键为 "alice"
的项,其值是对 Person
类型的 p1
对象的引用。如果我们修改 p1
的属性:
p1.age = 31
那么通过字典访问这个对象时,我们会发现其年龄也被更新了:
print(people_dict["alice"].age) # 输出:31
这是因为字典中存储的并不是 Person
对象的独立副本,而是指向同一内存地址的引用。
深拷贝与浅拷贝的区别
在涉及嵌套数据结构或自定义对象时,这种引用行为可能会导致意外的结果。例如,如果自定义对象中包含可变类型的属性(如列表或另一个自定义对象),直接将这样的对象存入字典并对其进行修改,会影响到通过字典获取的对象。
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
# 修改原始地址对象
address.city = "Shelbyville"
# 字典中的人的地址也变了
print(people_dict["bob"].address.city) # 输出:Shelbyville
解决办法:深拷贝
为了避免这种共享状态带来的问题,有时我们需要确保字典存储的是对象的一个完整副本,而不是引用。Python 提供了 copy
模块中的 deepcopy
函数来实现这一目标:
import copy
# 使用深拷贝存储对象
people_dict["bob_deepcopy"] = copy.deepcopy(p1)
# 此时即使修改原始地址对象,深拷贝的对象不会受影响
address.city = "Capital City"
print(people_dict["bob"].address.city) # 输出:Capital City
print(people_dict["bob_deepcopy"].address.city) # 输出:Shelbyville
总之,在 Python 中利用字典存储自定义对象时,务必注意默认情况下存储的是对象引用。对于那些需要保持独立状态的情况,请使用 deepcopy
进行深拷贝,以避免因共享引用而导致的意料之外的数据变化。