深拷贝与浅拷贝

深拷贝与浅拷贝的概念

使用时需要导入 copy 模块

  1. 浅拷贝 (Shallow Copy)
    • 通过复制对象生成一个新的对象,但只复制对象的引用。
    • 对于容器对象(如列表、字典等),它只复制第一层的元素。
    • 对于嵌套的可变对象(如列表中的列表),它们仍然指向原对象的内存地址。
    • 在 Python 中,使用 copy.copy() 实现。
  2. 深拷贝 (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]]
深拷贝与浅拷贝

输出分析:

  1. ID 对比
    • original_listshallow_copied_list 和 deep_copied_list 的 ID 不同,这证明它们是不同的对象。
  2. 对第一层元素的修改
    • 当我们修改 original_list[0] 为 'changed',这一变化不会影响shallow_copied_listdeep_copied_list,因为这仅改变了第一层的元素。
    • shallow_copied_list 和 deep_copied_list 保持原样,分别输出为 [1, 2, [3, 4]]
  3. 对嵌套列表的修改
    • 修改了 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],因为它是一个完全独立的副本。
  4. 对深拷贝的修改
    • 最后,我们对 deep_copied_list 中的内容进行修改。改变 deep_copied_list[2][1] 为 'deep_changed',这不会影响 original_list 或 shallow_copied_list,而 deep_copied_list 变为 [1, 2, [3, 'deep_changed']]
  5. 对浅拷贝的修改:
    • 当您用 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

Like (0)
LJH的头像LJH
Previous 19小时前
Next 2023年4月5日 下午10:51

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注