Python 之 深拷贝和浅拷贝 1

来源:互联网 发布:淘宝达管家 编辑:程序博客网 时间:2024/05/29 07:06
python 引用:
python一般内部赋值变量的话,都是传个引用变量,和C语言的传地址的概念差不多,
比如
>>>a = [1,2,3],表示变量a保存了这个列表的地址
python里可以用id()来查询下:a在内存的地址是:675375852
>>>b = a 
那b的内容是什么,地址又是什么呢?
用print 输出下b的内容也是[1,2,3],然后我们查看下b的地址看下能否验证我们的结论
>>>print id(b)

果然b的地址也是:675375852

呵呵看来结论也正确的。

这样会带来一个问题,因为变量a,和变量b都是保存了同一个列表的地址。如果我改变a指向的列表的值的话,
那b指向的列表的值也同时改变
比如:
>>>a[1] = 6
>>>print a
输出的内容是:[1,6,3]
>>>print b
b指向的列表的内容也是:[1,6,3]

python 拷贝

如果我们只想修改a列表里面的内容。而不想修改b的内容,那就要用到python的拷贝了
>>>a=[1,2,3]
>>>b=a[:]###拷贝了一份a的内容给b
>>>a[1]=6
>>>print a
输出a的内容是:[1,6,3]
而b的内容是:[1,2,3]

2个对象的地址不一样,说明是重新拷贝生成的一个新的列表对象,而不是引用的赋值。

[python] view plain copy
  1. import copy  
  2.   
  3. # copy 和 deepcopy 的区别  
  4.   
  5. a1 = [123, ['a''b']]  
  6. # copy 浅复制,不会拷贝其子对象,修改子对象,a2将受影响  
  7. a2 = copy.copy(a1)  
  8.   
  9. # deepcopy 深复制,将拷贝其子对象,修改子对象,a3将不受影响  
  10. a3 = copy.deepcopy(a1)  
  11.   
  12. a1[3].append('c')  
  13.   
  14. print 'a1:', a1  
  15. print 'a2:', a2  
  16. print 'a3:', a3  
输出:
a1: [1, 2, 3, ['a', 'b', 'c']]
a2: [1, 2, 3, ['a', 'b', 'c']]
a3: [1, 2, 3, ['a', 'b']]

本文通过几个实例来说明Python中list的深复制和浅复制:

复制代码
>>> a = [[]] * 10>>> a[[], [], [], [], [], [], [], [], [], []]>>> a[0][0] = 10    #NO WAYTraceback (most recent call last):  File "<stdin>", line 1, in <module>IndexError: list assignment index out of range>>> a[0].append(1)>>> a[[1], [1], [1], [1], [1], [1], [1], [1], [1], [1]]
复制代码

  a[0].append(1)后, 如果a的输出结果让你感到有些困惑,你可以参考这里(原因是Python中的*运算采用的是浅复制).

  同样的道理,下面的代码我们应该都能够理解:

复制代码
>>> a[3].append(9)>>> a[[1, 9], [1, 9], [1, 9], [1, 9], [1, 9], [1, 9], [1, 9], [1, 9], [1, 9], [1, 9]]>>> a[2][1] = 3>>> a[[1, 3], [1, 3], [1, 3], [1, 3], [1, 3], [1, 3], [1, 3], [1, 3], [1, 3], [1, 3]]
复制代码

  让我们一起来分析一下:

  对于a(理解为一个2维数组)中的每一个元素都是一个list(理解为一个1维数组), 但我们需要注意的是a的每一个元素

a[0],a[1],a[2]...指向的是同一段内存区域(浅复制),所以更改(修改值或添加值)任何一个元素(a[0]或a[1]...a[9])都会直接影

响到其它的元素.

  如何验证a中的每一个元素a[0], a[1],...,a[9]指向同一段内存区域? 可以通过id方法来验证:

>>> id.__doc__"id(object) -> integer

Return the identity of an object. This is guaranteed to be unique among
simultaneously existing objects. (Hint: it's the object's memory address.)
"

 

复制代码
>>> a = [[]]*10>>> a[[], [], [], [], [], [], [], [], [], []]>>> id(a[0])3071938316L>>> id(a[1])3071938316L>>> a[0].append(1)>>> a[[1], [1], [1], [1], [1], [1], [1], [1], [1], [1]]>>> id(a[0])3071938316L>>> id(a[1])3071938316L>>> 
复制代码

注意:虽然a的每一个元素a[0],a[1],a[2]...指向的是同一段内存区域,但a中的各个元素是独立的元素(他们相同但不同一)

也就是说删除掉任何一个数据对其他的数据没有任何影响:

复制代码
>>> a = [[]] * 10>>> a[[], [], [], [], [], [], [], [], [], []]>>> a[0].append(1)>>> a[[1], [1], [1], [1], [1], [1], [1], [1], [1], [1]]>>> len(a)10>>> del(a[2])>>> a[[1], [1], [1], [1], [1], [1], [1], [1], [1]]>>> len(a)9
复制代码

  那么应该怎么实现深复制呢?其实在前面提到的文章中已经介绍了这一方法:  

复制代码
>>> c = [[] for i in range(10)]>>> c[[], [], [], [], [], [], [], [], [], []]>>> c[0].append(3)>>> c[[3], [], [], [], [], [], [], [], [], []]>>> 
复制代码

  至此, 我觉得还有一点需要说明:

  一定要理解*操作的对象是谁, 例如: [2]*10得到[2, 2, 2, 2, 2, 2, 2, 2, 2, 2], *10操作的对象是[]中的2, 也就是说*10操

作使list中的元素2复制10次. 同理[[]]*10得到[[], [], [], [], [], [], [], [], [], []],*10操作的对象是[]中的[], 也就是说*10

操作使list中的元素[]浅复制10次, 这10个空list指向内存中相同的区域(参见上面用id验证部分).

  下面用2段代码作为对比列出来, 便于查看:

复制代码
>>> a = [2] * 10>>> a[2, 2, 2, 2, 2, 2, 2, 2, 2, 2]>>> id(a[0])164067492>>> id(a[1])164067492>>> a[0] = 1  #NOTE>>> id(a[0])  #NOTE164067504>>> id(a[1])164067492>>> 
复制代码

 

复制代码
>>> b = [[]] * 10>>> b[[], [], [], [], [], [], [], [], [], []]>>> id(b[0])3072965964L>>> id(b[1])3072965964L>>> b[0].append(10)>>> id(b[0])3072965964L>>> id(b[1])3072965964L>>> b[[10], [10], [10], [10], [10], [10], [10], [10], [10], [10]]>>> b[0][0] = 1>>> b[[1], [1], [1], [1], [1], [1], [1], [1], [1], [1]]>>> id(b[0])3072965964L>>> id(b[1])3072965964L>>> b[0] = [10]>>> b[[10], [1], [1], [1], [1], [1], [1], [1], [1], [1]]>>> id(b[1])3072965964L>>> id(b[0])3072965996L>>> 
复制代码

0 0