python 为什么有深拷贝浅拷贝 python深拷贝和浅拷贝的区别

python\u4e2d\u6d45\u62f7\u8d1d\u548c\u6df1\u62f7\u8d1d\u7684\u533a\u522b

python\u4e00\u822c\u6709\u4e09\u79cd\u62f7\u8d1d\u65b9\u6cd5\u3002
\u4ee5alist=[1,2,3,["a","b"]]\u4e3a\u4f8b:
\uff081\uff09\u76f4\u63a5\u8d4b\u503c\u3002\u4f20\u9012\u5bf9\u8c61\u7684\u5f15\u7528\u800c\u5df2,\u539f\u59cb\u5217\u8868\u6539\u53d8\uff0c\u88ab\u8d4b\u503c\u7684b\u4e5f\u4f1a\u505a\u76f8\u540c\u7684\u6539\u53d8
>>> b=alist
>>> print b
[1, 2, 3, ['a', 'b']]
>>> alist.append(5)
>>> print alist;print b
[1, 2, 3, ['a', 'b'], 5]
[1, 2, 3, ['a', 'b'], 5]
\uff082\uff09copy\u6d45\u62f7\u8d1d\u3002\u6ca1\u6709\u62f7\u8d1d\u5b50\u5bf9\u8c61\uff0c\u6240\u4ee5\u539f\u59cb\u6570\u636e\u6539\u53d8\uff0c\u5b50\u5bf9\u8c61\u4f1a\u6539\u53d8\u3002
>>> import copy
>>> c=copy.copy(alist)
>>> print alist;print c
[1, 2, 3, ['a', 'b']]
[1, 2, 3, ['a', 'b']]
>>> alist.append(5)
>>> print alist;print c
[1, 2, 3, ['a', 'b'], 5]
[1, 2, 3, ['a', 'b']]
>>> alist[3]
['a', 'b']
>>> alist[3].append('cccc')
>>> print alist;print c
[1, 2, 3, ['a', 'b', 'cccc'], 5]
[1, 2, 3, ['a', 'b', 'cccc']] \u91cc\u9762\u7684\u5b50\u5bf9\u8c61\u88ab\u6539\u53d8\u4e86
\uff083\uff09\u6df1\u62f7\u8d1d\u3002\u5305\u542b\u5bf9\u8c61\u91cc\u9762\u7684\u81ea\u5bf9\u8c61\u7684\u62f7\u8d1d\uff0c\u6240\u4ee5\u539f\u59cb\u5bf9\u8c61\u7684\u6539\u53d8\u4e0d\u4f1a\u9020\u6210\u6df1\u62f7\u8d1d\u91cc\u4efb\u4f55\u5b50\u5143\u7d20\u7684\u6539\u53d8\u3002
>>> import copy
>>> d=copy.deepcopy(alist)
>>> print alist;print d
[1, 2, 3, ['a', 'b']]
[1, 2, 3, ['a', 'b']]\u59cb\u7ec8\u6ca1\u6709\u6539\u53d8
>>> alist.append(5)
>>> print alist;print d
[1, 2, 3, ['a', 'b'], 5]
[1, 2, 3, ['a', 'b']]\u59cb\u7ec8\u6ca1\u6709\u6539\u53d8
>>> alist[3]
['a', 'b']
>>> alist[3].append("ccccc")
>>> print alist;print d
[1, 2, 3, ['a', 'b', 'ccccc'], 5]
[1, 2, 3, ['a', 'b']] \u59cb\u7ec8\u6ca1\u6709\u6539\u53d8

\u6df1\u5ea6\u62f7\u8d1d\uff0c\u5c31\u662f\u628a\u62f7\u8d1d\u5bf9\u8c61\u91cc\u6240\u6709\u7684\u4e1c\u897f\u5168\u90e8\u53e6\u5b58\u4e00\u4efd\u5230\u65b0\u7684\u5185\u5b58\u7a7a\u95f4\uff0c\u62f7\u8d1d\u5b8c\u540e\uff0c\u4e24\u8005\u6beb\u65e0\u8054\u7cfb\uff0c\u4ece\u6b64\u662f\u8def\u4eba\u3002\u8ddf\u6211\u4eec\u7684\u5e38\u8bc6\u7406\u89e3\u662f\u4e00\u56de\u4e8b\u3002
\u6d45\u62f7\u8d1d\uff0c\u5c31\u662f\u628a\u62f7\u8d1d\u5bf9\u8c61\u7684\u5730\u5740\u7ed9\u4e86\u9700\u8981\u62f7\u8d1d\u7684\u5bf9\u8c61\uff0c\u770b\u4e0a\u53bb\u4e24\u4e2a\u662f\u4e0d\u540c\u7684\u5bf9\u8c61\uff0c\u5176\u5b9e\u672c\u8d28\u90fd\u662f\u4e00\u56de\u4e8b\uff0c\u4e5f\u6709\u53e6\u5916\u4e00\u79cd\u6d45\u62f7\u8d1d\uff0c\u770b\u4e0a\u53bb\uff0c\u786e\u5b9e\u590d\u5236\u4e86\u4e00\u4efd\u65b0\u7684\uff0c\u4f46\u8fd9\u4e2a\u65b0\u7684\u5bf9\u8c61\uff0c\u662f\u4e2a\u53ef\u53d8\u5bf9\u8c61\uff0c\u5b83\u6307\u5411\u7684\u5bf9\u8c61\u5185\u5bb9\uff0c\u4ecd\u7136\u6ca1\u6709\u590d\u5236\u8fc7\u6765\uff0c\u4ecd\u7136\u6307\u5411\u4e86\u540c\u4e00\u4e2a\u4e1c\u897f\u3002

在写Python过程中,经常会遇到对象的拷贝,如果不理解浅拷贝和深拷贝的概念,你的代码就可能出现一些问题。所以,在这里按个人的理解谈谈它们之间的区别。

一、赋值(assignment)
在《Python FAQ1》一文中,对赋值已经讲的很清楚了,关键要理解变量与对象的关系。

12345
>>> a = [1, 2, 3]>>> b = a>>> print(id(a), id(b), sep='\n')139701469405552139701469405552

在Python中,用一个变量给另一个变量赋值,其实就是给当前内存中的对象增加一个“标签”而已。
如上例,通过使用内置函数 id() ,可以看出 a 和 b 指向内存中同一个对象。a is b会返回 True 。

二、浅拷贝(shallow copy)
注意:浅拷贝和深拷贝的不同仅仅是对组合对象来说,所谓的组合对象就是包含了其它对象的对象,如列表,类实例。而对于数字、字符串以及其它“原子”类型,没有拷贝一说,产生的都是原对象的引用。
所谓“浅拷贝”,是指创建一个新的对象,其内容是原对象中元素的引用。(拷贝组合对象,不拷贝子对象)
常见的浅拷贝有:切片操作、工厂函数、对象的copy()方法、copy模块中的copy函数。

12345678910
>>> a = [1, 2, 3]>>> b = list(a)>>> print(id(a), id(b)) # a和b身份不同140601785066200 140601784764968>>> for x, y in zip(a, b): # 但它们包含的子对象身份相同... print(id(x), id(y))... 140601911441984 140601911441984140601911442016 140601911442016140601911442048 140601911442048

从上面可以明显的看出来,a 浅拷贝得到 b,a 和 b 指向内存中不同的 list 对象,但它们的元素却指向相同的 int 对象。这就是浅拷贝!

三、深拷贝(deep copy)
所谓“深拷贝”,是指创建一个新的对象,然后递归的拷贝原对象所包含的子对象。深拷贝出来的对象与原对象没有任何关联。
深拷贝只有一种方式:copy模块中的deepcopy函数。
1234567891011
>>> import copy>>> a = [1, 2, 3]>>> b = copy.deepcopy(a)>>> print(id(a), id(b))140601785065840 140601785066200>>> for x, y in zip(a, b):... print(id(x), id(y))... 140601911441984 140601911441984140601911442016 140601911442016140601911442048 140601911442048

看了上面的例子,有人可能会疑惑:
为什么使用了深拷贝,a和b中元素的id还是一样呢?
答:这是因为对于不可变对象,当需要一个新的对象时,python可能会返回已经存在的某个类型和值都一致的对象的引用。而且这种机制并不会影响 a 和 b 的相互独立性,因为当两个元素指向同一个不可变对象时,对其中一个赋值不会影响另外一个。
我们可以用一个包含可变对象的列表来确切地展示“浅拷贝”与“深拷贝”的区别:

1234567891011121314151617181920
>>> import copy>>> a = [[1, 2],[5, 6], [8, 9]]>>> b = copy.copy(a) # 浅拷贝得到b>>> c = copy.deepcopy(a) # 深拷贝得到c>>> print(id(a), id(b)) # a 和 b 不同139832578518984 139832578335520>>> for x, y in zip(a, b): # a 和 b 的子对象相同... print(id(x), id(y))... 139832578622816 139832578622816139832578622672 139832578622672139832578623104 139832578623104>>> print(id(a), id(c)) # a 和 c 不同139832578518984 139832578622456>>> for x, y in zip(a, c): # a 和 c 的子对象也不同... print(id(x), id(y))... 139832578622816 139832578621520139832578622672 139832578518912139832578623104 139832578623392

从这个例子中可以清晰地看出浅拷贝与深拷贝地区别。

总结:
1、赋值:简单地拷贝对象的引用,两个对象的id相同。
2、浅拷贝:创建一个新的组合对象,这个新对象与原对象共享内存中的子对象。
3、深拷贝:创建一个新的组合对象,同时递归地拷贝所有子对象,新的组合对象与原对象没有任何关联。虽然实际上会共享不可变的子对象,但不影响它们的相互独立性。
浅拷贝和深拷贝的不同仅仅是对组合对象来说,所谓的组合对象就是包含了其它对象的对象,如列表,类实例。而对于数字、字符串以及其它“原子”类型,没有拷贝一说,产生的都是原对象的引用。

扩展阅读:c++和python先学哪个 ... python for死循环 ... python初学编程必背 ... python代码自动生成器 ... python编程入门自学 ... python 跳出循环体 ... python与c++学哪个好 ... python 深拷贝 浅拷贝 ... 浅拷贝的三种实现方式 ...

本站交流只代表网友个人观点,与本站立场无关
欢迎反馈与建议,请联系电邮
2024© 车视网