文章目录
- 直接引用
- 使用方法
- 举例说明
- 浅拷贝
- 使用方法
- 举例说明
- 深拷贝
- 使用方法
- 举例说明
- 三种方式如何选择
- 举例说明
直接引用
直接引用意味着你创建了一个新的变量,这个变量指向已存在对象的内存地址。
使用方法
# 直接赋值
a = [1, 2, 3]
b = a
id(b) == id(a)
因此,如果你修改了任何一个变量所指向的对象,所有指向该对象的变量都会反映这个改变。
举例说明
a = [1, 2, 3]
b = a # b是a的直接引用 id(b) == id(a)
b.append(4) # 修改b
print(a) # 输出: [1, 2, 3, 4],a也被修改了
浅拷贝
浅拷贝创建了一个新对象,但不会递归地复制对象中的内部对象;它仅仅复制了容器中的元素的引用。
使用方法
a = [1, 2, 3] # 元素不可变
a = [[1, 2], [3, 4]] # 元素可变
# 方法一
b = a[:] # 或 b = a[0:]
# 方法二
import copy
b = copy.copy(a)
id(b) != id(a)
id(b[0]) == id(a[0])
如果容器中的元素是不可变的(比如整数、字符串等),这通常不是问题。但如果容器中包含了可变对象,那么这些对象不会被复制,只是它们的引用被复制了。
举例说明
import copy
a = [[1, 2], [3, 4]]
b = copy.copy(a) # 浅拷贝a
b[0].append(5) # 修改b中的一个子列表
print(a) # 输出: [[1, 2, 5], [3, 4]],a中的相应子列表也被修改了
深拷贝
深拷贝创建了一个新对象,然后递归地复制了原始对象中的所有元素及其子元素。
使用方法
a = [[1, 2], [3, 4]]
import copy
b = copy.deepcopy(a)
id(b) != id(a)
id(b[0]) != id(a[0])
因此,原始对象和新对象完全独立,修改一个不会影响另一个。
举例说明
a = [[1, 2], [3, 4]]
b = copy.deepcopy(a) # 深拷贝a
b[0].append(5) # 修改b中的一个子列表
print(a) # 输出: [[1, 2], [3, 4]],a没有被修改
三种方式如何选择
如果需要拷贝a的是一个一维列表,且其元素(整数)是不可变的,使用浅拷贝(例如 a[:] 或 a[0:])就足够了。这样做能有效地复制列表及其内容,而不需要深拷贝的额外开销。
另外,浅拷贝和深拷贝的概念不限于列表,它们适用于Python中的所有容器类型以及更复杂的对象。这包括但不限于列表、字典、集合以及包含其他对象的自定义类的实例。这两种拷贝方式在处理复合对象(即包含其他对象的对象)时非常重要,因为它们决定了原始数据和新数据之间的独立性。
对于非容器类型的元素(如整数、浮点数、字符串等不可变类型),浅拷贝和深拷贝的效果相同,因为不可变类型不会被“拷贝”——它们是通过引用共享的。
举例说明
考虑一个包含另一个对象作为属性的自定义类。在这种情况下,浅拷贝和深拷贝之间的差异变得尤为重要。
import copy
class MyClass:
def __init__(self, value):
self.value = value
class Container:
def __init__(self, contained):
self.contained = contained
obj1 = MyClass(10)
container = Container(obj1)
# 浅拷贝
shallow_copied_container = copy.copy(container)
shallow_copied_container.contained.value = 20
print(container.contained.value) # 输出 20,因为 contained 被浅拷贝,所以它仍然引用原始对象
# 深拷贝
obj2 = MyClass(10)
container = Container(obj2)
deep_copied_container = copy.deepcopy(container)
deep_copied_container.contained.value = 30
print(container.contained.value) # 输出 10,深拷贝创建了 contained 的一个独立副本
这个例子展示了对于包含其他对象的复杂对象,深拷贝如何能够确保所有层级的数据都被复制,而浅拷贝则只复制最外层的容器。因此,深拷贝和浅拷贝的选择取决于你想要的拷贝深度以及对象之间是否需要独立。