深拷贝与浅拷贝的概念
使用时需要导入 copy 模块
- 浅拷贝 (Shallow Copy):
- 通过复制对象生成一个新的对象,但只复制对象的引用。
- 对于容器对象(如列表、字典等),它只复制第一层的元素。
- 对于嵌套的可变对象(如列表中的列表),它们仍然指向原对象的内存地址。
- 在 Python 中,使用
copy.copy()
实现。
- 深拷贝 (Deep Copy):
- 创建一个完全独立的新对象,同时递归地复制原对象及其所有嵌套对象。
- 修改新对象的一部分不会影响到原对象。
- 在 Python 中,使用
copy.deepcopy()
实现。
如何使用深浅拷贝
浅拷贝示例
import copy
# 创建一个嵌套列表
original_list = [1, 2, [3, 4]]
# 使用浅拷贝
shallow_copied_list = copy.copy(original_list)
# 查看两者的id
print(id(original_list)) # 输出原列表的id
print(id(shallow_copied_list)) # 输出浅拷贝列表的id
# 修改嵌套列表
original_list[2][0] = 'changed'
print(original_list) # 输出: [1, 2, ['changed', 4]]
print(shallow_copied_list) # 输出: [1, 2, ['changed', 4]]

original_list
和shallow_copied_list
有不同的 ID,表示它们是不同的对象。- 当你改变
original_list
中嵌套列表的内容时,shallow_copied_list
中的相应内容也会变化,因为这两个列表共享同一个嵌套对象
深拷贝示例
import copy
# 创建一个嵌套列表
original_list = [1, 2, [3, 4]]
# 使用深拷贝
deep_copied_list = copy.deepcopy(original_list)
# 查看两者的id
print(id(original_list)) # 输出原列表的id
print(id(deep_copied_list)) # 输出深拷贝列表的id
# 修改嵌套列表
original_list[2][0] = 'changed'
print(original_list) # 输出: [1, 2, ['changed', 4]]
print(deep_copied_list) # 输出: [1, 2, [3, 4]]

original_list
和deep_copied_list
有不同的 ID,表示它们是不同的对象。- 当你改变
original_list
中嵌套列表的内容时,deep_copied_list
中的内容保持不变,因为深拷贝创建了嵌套对象的全新副本。
深浅拷贝修改不同层次上的元素
import copy
# 创建一个嵌套列表,包含整数和另一个包含整数的列表
original_list = [1, 2, [3, 4]]
# 使用浅拷贝
shallow_copied_list = copy.copy(original_list)
# 使用深拷贝
deep_copied_list = copy.deepcopy(original_list)
# 查看每个列表的ID
print("Original List ID:", id(original_list))
print("Shallow Copied List ID:", id(shallow_copied_list))
print("Deep Copied List ID:", id(deep_copied_list))
# 修改原始列表的第一层元素
original_list[0] = 'changed'
# 修改嵌套列表的第二层元素
original_list[2][0] = 'changed_nested'
print("\nAfter modifying original_list:")
print("Original List:", original_list) # 输出: ['changed', 2, ['changed_nested', 4]]
print("Shallow Copied List:", shallow_copied_list) # 输出: [1, 2, [3, 4]]
print("Deep Copied List:", deep_copied_list) # 输出: [1, 2, [3, 4]]
# 接下来修改深拷贝中的内容
deep_copied_list[2][1] = 'deep_changed'
print("\nAfter modifying deep_copied_list:")
print("Original List:", original_list) # 输出: ['changed', 2, ['changed_nested', 4]]
print("Shallow Copied List:", shallow_copied_list) # 输出: [1, 2, [3, 4]]
print("Deep Copied List:", deep_copied_list) # 输出: [1, 2, [3, 'deep_changed']]
# 修改浅拷贝中嵌套列表的内容
shallow_copied_list[2][0] = 'changed_from_shallow_copy'
print("After modifying shallow_copied_list:")
print("Original List:", original_list) # 输出: [1, 2, ['changed_from_shallow_copy', 4]]
print("Shallow Copied List:", shallow_copied_list) # 输出: [1, 2, ['changed_from_shallow_copy', 4]]

输出分析:
- ID 对比:
original_list
、shallow_copied_list
和deep_copied_list
的 ID 不同,这证明它们是不同的对象。
- 对第一层元素的修改:
- 当我们修改
original_list[0]
为'changed'
,这一变化不会影响shallow_copied_list
或deep_copied_list
,因为这仅改变了第一层的元素。 shallow_copied_list
和deep_copied_list
保持原样,分别输出为[1, 2, [3, 4]]
。
- 当我们修改
- 对嵌套列表的修改:
- 修改了
original_list[2][0]
为'changed_nested'
。由于shallow_copied_list
复制了原始列表中嵌套列表的引用,这次修改也影响到了shallow_copied_list
中的值。 - 因此,
shallow_copied_list
输出变为[1, 2, ['changed_nested', 4]]
。 - 而
deep_copied_list
中的相应值保持不变,仍为[3, 4]
,因为它是一个完全独立的副本。
- 修改了
- 对深拷贝的修改:
- 最后,我们对
deep_copied_list
中的内容进行修改。改变deep_copied_list[2][1]
为'deep_changed'
,这不会影响original_list
或shallow_copied_list
,而deep_copied_list
变为[1, 2, [3, 'deep_changed']]
。
- 最后,我们对
- 对浅拷贝的修改:
- 当您用
shallow_copied_list[2][0] = 'changed_from_shallow_copy'
改变了shallow_copied_list
中嵌套列表的第一个元素后,original_list
中相应的嵌套元素也会改变为'changed_from_shallow_copy'
。这表明两者共享相同的嵌套子列表的引用,因此对任何一个对象的修改都将影响到另一个。
- 当您用
使用场景
- 浅拷贝的场景:
- 当你只需要一个新对象,但想保持对一些共享数据的访问时。
- 性能要求高,入参对象较大,不需要复制嵌套对象。
- 深拷贝的场景:
- 当你需要一个完全独立且不受原对象影响的副本时。
- 处理复杂的嵌套数据结构时,如深层的JSON对象或自定义的数据结构。
自定义对象的拷贝
如果我们有一个自定义类,我们也可以实现深拷贝和浅拷贝。下面是一个示例:
import copy
class MyClass:
def __init__(self, value):
self.value = value
self.nested_list = [1, 2, [3, 4]]
def __repr__(self):
return f"MyClass(value={self.value}, nested_list={self.nested_list})"
# 创建对象
original_object = MyClass(10)
# 浅拷贝
shallow_copied_object = copy.copy(original_object)
# 深拷贝
deep_copied_object = copy.deepcopy(original_object)
# 修改嵌套列表
original_object.nested_list[2][0] = 'changed'
# 输出三个对象
print("Original Object:", original_object) # 输出: MyClass(value=10, nested_list=[1, 2, ['changed', 4]])
print("Shallow Copied Object:", shallow_copied_object) # 输出: MyClass(value=10, nested_list=[1, 2, ['changed', 4]])
print("Deep Copied Object:", deep_copied_object) # 输出: MyClass(value=10, nested_list=[1, 2, [3, 4]])
循环引用的处理
深拷贝能够安全地处理对象间的循环引用,而浅拷贝则不具备这样的能力。以下是演示的示例:
import copy
class Node:
def __init__(self, value):
self.value = value
self.next = None
# 创建节点
node1 = Node(1)
node2 = Node(2)
node1.next = node2
node2.next = node1 # 形成循环引用
# 浅拷贝
shallow_copied_node = copy.copy(node1)
# 深拷贝
deep_copied_node = copy.deepcopy(node1)
# 修改 original node1
node1.value = 'changed'
# 输出结果
print("Original Node:", node1.value, "->", node1.next.value) # 输出: changed -> 2
print("Shallow Copied Node:", shallow_copied_node.value, "->", shallow_copied_node.next.value) # 输出: changed -> 2
print("Deep Copied Node:", deep_copied_node.value, "->", deep_copied_node.next.value) # 输出: 1 -> 2
注意事项和潜在问题
- 性能:
- 深拷贝的性能成本较高,尤其对于大对象,因为它需要递归地复制所有嵌套对象。
- 浅拷贝相对较快,因为它只涉及到对引用的复制。
- 不可变对象:对于不可变对象(如字符串、元组),浅拷贝和深拷贝的效果是一样的,因为它们无法被修改。
import copy
# 创建一个字符串
original_string = "Hello, World!"
# 浅拷贝
shallow_copied_string = copy.copy(original_string)
# 深拷贝
deep_copied_string = copy.deepcopy(original_string)
# 由于字符串是不可变的,修改其中一个也不会影响另一个
original_string += " Goodbye!"
print("Original String:", original_string) # 输出: Original String: Hello, World! Goodbye!
print("Shallow Copied String:", shallow_copied_string) # 输出: Shallow Copied String: Hello, World!
print("Deep Copied String:", deep_copied_string) # 输出: Deep Copied String: Hello, World!
当执行 original_string += " Goodbye!"
时,Python 实际上是创建了一个新的字符串对象,即 Hello, World! Goodbye!
,并将其赋值给 original_string
。这样,shallow_copied_string
和 deep_copied_string
保持不变,因为它们仍然指向原来的 Hello, World!
。
import copy
# 创建一个元组
original_tuple = (1, 2, (3, 4))
# 浅拷贝
shallow_copied_tuple = copy.copy(original_tuple)
# 深拷贝
deep_copied_tuple = copy.deepcopy(original_tuple)
# 修改元组是不可变的,所以不能直接改变其中的元素
# 但可以创建新的元组来赋值
new_tuple = original_tuple + (5,)
print("Original Tuple:", original_tuple) # 输出: Original Tuple: (1, 2, (3, 4))
print("Shallow Copied Tuple:", shallow_copied_tuple) # 输出: Shallow Copied Tuple: (1, 2, (3, 4))
print("Deep Copied Tuple:", deep_copied_tuple) # 输出: Deep Copied Tuple: (1, 2, (3, 4))
print("New Tuple:", new_tuple) # 输出: New Tuple: (1, 2, (3, 4), 5)
- 自定义对象的拷贝:
- 如果你有自定义对象,使用深拷贝可能需要重写
__deepcopy__
方法,以控制如何拷贝该对象的属性。
- 如果你有自定义对象,使用深拷贝可能需要重写
class MyClass:
def __init__(self, value):
self.value = value
def __deepcopy__(self, memo):
# 自定义深拷贝逻辑
new_obj = MyClass(copy.deepcopy(self.value, memo))
return new_obj
- 循环引用:
- 深拷贝处理循环引用时会比较安全,而浅拷贝可能会导致多个引用指向同一个对象。
deepcopy
会在内部使用一个 memo 字典来避免无限递归,确保每个对象只被复制一次。
发布者:LJH,转发请注明出处:https://www.ljh.cool/43137.html