首页 > 学院 > 开发设计 > 正文

拷贝Python对象:深拷贝与浅拷贝

2019-11-14 11:09:43
字体:
来源:转载
供稿:网友

[Python][6]中的拷贝概念与[C++][6]中的一样。也即深拷贝就是对对象资源的拷贝,浅拷贝就是对引用的拷贝。这与我们直觉中的拷贝有点不一样,所以在实际应用中容易搞混。


一、熟悉python内存管理

在python中,变量名不用事先声明,变量类型也不用事先声明,变量会在第一次赋值时自动声明,在创建时,也就是赋值的时候,解释器会根据语法和右侧的操作数来决定新对象的类型。

要保持追踪内存中的对象,Python使用引用计数这一简单技术。也就是说python内部记录着所有使用中的对象各有多少引用。当对象被创建时,就创建一个引用计数,并且被设置为1(事实上它并不是1,可能是python本身对创建的对象有引用)。

>>> x = 123 #新创建的整型对象123赋值给x,其引用计数为1>>> y = x #y是x的别名,现在整型对象123的引用计数为2

二. Python 的复制

先看代码:

>>> a = [1,2,3]>>> b = a>>> b.append(111)>>> PRint(a,b)>>> [1,2,3,111] [1,2,3,111]>>>print(id(a),id(b))>>> 64880112 64880112

从上面可见,对象的赋值实际上是对象的引用。当创建一个对象,然后把它赋给另一个变量的时候,python并没有拷贝这个对象,而只是拷贝了这个对象的引用。

如果你想修改一个对象,而且想让原始的对象不受影响,那你就需要对象复制。可以使用如下几个方法:

(1)、使用切片[:]操作进行拷贝 (2)、使用工厂函数(如list/dir/set)等进行拷贝 (3)、copy.copy()(需导入copy模块)

>>> person = ["name",["save",100]]>>> tom = person[:]>>> jack = list(person)>>> tom['name', ['save', 100]]>>> jack['name', ['save', 100]]>>> [id(x) for x in (person,tom,jack)][52590472, 63910176, 67064136]

在上面的代码中,我们采用切片和工厂函数list进行拷贝,可以看到拷贝后的tom,jack,person的id值均不同,那这是否是已经达到我们想要的拷贝呢?

现在我们对拷贝的tom,jack,进行一些操作:

>>> tom[0] = "tom">>> jack[0] = "jack">>> tom['tom', ['save', 100]]>>> jack['jack', ['save', 100]]>>> tom[1][1] = 50>>> tom['tom', ['save', 50]]>>> jack['jack', ['save', 50]]

上面的实例中,我们成功修改 了姓名,但是对存款的修改却没有达到预想的效果(前提是我们希望复制后的tom,jack 互不影响)。 原因是我们只做了浅拷贝。对一个对象进行浅拷贝其实是新创建了一个类型跟原对象一样,其内容是原对象元素的引用,换句话说,这个拷贝对象本身是新的,但是它的内容不是。 那么为什么修改姓名时没有互相影响,而修改存款时会互相影响?这是因为在这两个列表对象中,第一个对象是不可变对象(是个字符串类型),第二个对象是可变对象(一个列表)。 在python中字符串不可以修改,所以在为tom和jack重新命名的时候,会重新创建一个’tom’和“jack”对象,替换旧的’name’对象。这就说明了,浅复制(shallow copy),它复制了对象,但对于对象中的元素,依然使用引用.

>>> import copy>>> aa = [1,2,3]>>> bb =copy.copy(aa)>>> id(aa)63911536>>> id(bb)67064176>>> bb[0] = 100>>> aa[1, 2, 3]>>> bb #由于数字不可变,修改的时候会替换旧的对象[100, 2, 3]

下面试试复制的对象中包含可变对象:

>>> lis = [[1],["aaa"]]>>> clis = copy.copy(lis)>>> lis[[1], ['aaa']]>>> clis[[1], ['aaa']]>>> clis[0].append("bbbb")>>> lis[[1, 'bbbb'], ['aaa']]>>> clis[[1, 'bbbb'], ['aaa']]

上面的示例中,复制后的clis的修改影响到了原来的lis,这并不是我们想要的。如果希望复制一个容器对象,以及它里面的所有元素(包含元素的子元素),使用copy.deepcopy,这个方法会消耗一些时间和空间,不过,如果你需要完全复制,这是唯一的方法.

#深拷贝>>> deeplis = copy.deepcopy(lis)>>> deeplis[[1, 'bbbb'], ['aaa']]>>> deeplis[0].append("aaaa")>>> deeplis[[1, 'bbbb', 'aaaa'], ['aaa']]>>> lis[[1, 'bbbb'], ['aaa']]

注意:

1、对于非容器类型(如数字、字符串、和其他‘原子’类型的对象)没有被拷贝一说。

2、如果元祖变量只包含原子类型对象,则不能深copy。

参考:http://www.VEVb.com/BeginMan/p/3197649.html


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表