From: B座17楼 2022-06-23 10:12 发表于重庆
--------------------------------------------------------------
copy()
深浅的区别
doesn’t always really copy 并不总是复制要复制一个 list 或 dict,可以写 my_list.copy()
或 my_dict.copy()
到此为止,一切顺利。但是,如果你认为对原始版本的更改不会影响到副本,那么遗憾地告诉你,错了。copy()
会创建一个新的列表,但列表中的每个 dict 都是对原始列表中同一对象的引用。下图显示了对 list-of-dicts 进行浅复制和深复制(真正进行复制)的区别。
from copy import deepcopy
my_list = [{"one": 1}]
# # 创建两个地址并不同的副本
shallow_copy = my_list.copy()
deep_copy = deepcopy(my_list)
# 更改原始数据
my_list.append({"two": 2})
my_list[0]["one"] = 77
# 查看更改
assert my_list == [{"one": 77}, {"two": 2}]
assert shallow_copy == [{"one": 77}] # Mutated!
assert deep_copy == [{"one": 1}]
------------------------------------------------------
赋值,浅拷贝,还是深拷贝?
一个关于Python内存管理的故事
from copy import copy, deepcopy
这篇文章的目的是描述当我们在内存中发生什么时
赋值一个变量B = A 。
浅层拷贝它 C = copy(A) , 或
深度拷贝 D = deepcopy(A)。
我首先描述了一点关于Python中的内存管理和优化。在打好基础之后,我解释了赋值语句、浅拷贝和深拷贝之间的区别。然后我用一个表格总结了这些区别。
Python中的内存管理
int, float, list, dict, class instances, ...它们都是Python中的对象。在CPython的实现中,内置函数id()返回一个对象的内存地址--
>>> L1 = [1, 2, 3]>> id(L1)3061530120
>>> L2 = [1, 2, 3]>>> id(L2)3061527304每当一个新的对象被创建,它将有一个新的内存地址。除了当它是--一个非常短的字符串一个在[-5, 256]内的整数一个空的不可变的容器(例如 元组)。让我们看一个整数对象的例子。x和y都指的是同一个值10。在前面的例子中,L1和L2有两个不同的内存地址,而x和y则共享同一个内存地址-->>> x = 10>>> y = 10>>> id(x)2301840>>> id(y)2301840这是因为,在这三个例外中,Python 通过让第二个变量引用内存中的同一个对象来优化内存,--有人称之为 "共享对象"。请记住共享对象的概念,因为我们以后在创建一个对象的深拷贝时将需要它。变量赋值在 Python 文档中说:"Python 中的赋值语句不是复制对象,而是在目标和对象之间建立绑定。" 这意味着当我们通过赋值创建一个变量时,新的变量指的是与原始变量相同的对象----。>>> A = [1, 2, [10, 11], 3, [20, 21]]>> B = A>>> id(A)3061527080>>> id(B)3061527080
>>> id(A[2])3061527368>>> id(B[2])3061527368
如下图所示,A和B共享相同的id,即它们在内存中引用的是同一个对象。而且它们也包含相同的元素。
浅层拷贝当我们通过浅层拷贝创建一个变量时,新的变量会指向一个新的对象--
>>> A = [1, 2, [10, 11], 3, [20, 21]]>> C = copy(A)>>> id(A)3062428488
>> id(C)3062428520
>>> id(A[0])2301696
>>> id(C[0])2301696
>>> id(A[2])3062464904
>>> id(C[2])3062464904
下图说明了A中的元素如何与C中的元素指代相同的对象。深度复制>>> A = [1, 2, [10, 11], 3, [20, 21]]>> D = deepcopy(A)>>> id(A)3062727496>> id(D)3062428488正如Python文档中描述的那样--浅拷贝和深拷贝的区别只与复合对象 (包含其他对象的对象,如列表或类实例) 有关。- 浅拷贝构造一个新的复合对象,然后 (尽可能地) 在其中插入对原始对象的引用。- 深度拷贝构造一个新的复合对象,然后递归地将原始对象的副本插入其中。>>> id(A[0])2301696
>>> id(D[0])2301696
>>> id(A[2])3062464648
>>> id(D[2])3062466376但是为什么A[0]和D[0]共享同一个对象(即有相同的内存地址)?因为它们都引用了整数,这是我们在开始时提到的由于内存优化而出现的三种例外情况之一。下图显示A和D指的是内存中两个不同的列表,A中的元素与D中的元素指的是不同的对象,除了由于内存优化的整数元素。