拷贝Python对象:深拷贝与浅拷贝
2017-02-04 11:03
253 查看
[Python][6]中的拷贝概念与[C++][6]中的一样。也即深拷贝就是对对象资源的拷贝,浅拷贝就是对引用的拷贝。这与我们直觉中的拷贝有点不一样,所以在实际应用中容易搞混。
要保持追踪内存中的对象,Python使用引用计数这一简单技术。也就是说python内部记录着所有使用中的对象各有多少引用。当对象被创建时,就创建一个引用计数,并且被设置为1(事实上它并不是1,可能是python本身对创建的对象有引用)。
从上面可见,对象的赋值实际上是对象的引用。当创建一个对象,然后把它赋给另一个变量的时候,python并没有拷贝这个对象,而只是拷贝了这个对象的引用。
如果你想修改一个对象,而且想让原始的对象不受影响,那你就需要对象复制。可以使用如下几个方法:
(1)、使用切片[:]操作进行拷贝
(2)、使用工厂函数(如list/dir/set)等进行拷贝
(3)、copy.copy()(需导入copy模块)
在上面的代码中,我们采用切片和工厂函数list进行拷贝,可以看到拷贝后的tom,jack,person的id值均不同,那这是否是已经达到我们想要的拷贝呢?
现在我们对拷贝的tom,jack,进行一些操作:
上面的实例中,我们成功修改 了姓名,但是对存款的修改却没有达到预想的效果(前提是我们希望复制后的tom,jack 互不影响)。
原因是我们只做了浅拷贝。对一个对象进行浅拷贝其实是新创建了一个类型跟原对象一样,其内容是原对象元素的引用,换句话说,这个拷贝对象本身是新的,但是它的内容不是。
那么为什么修改姓名时没有互相影响,而修改存款时会互相影响?这是因为在这两个列表对象中,第一个对象是不可变对象(是个字符串类型),第二个对象是可变对象(一个列表)。
在python中字符串不可以修改,所以在为tom和jack重新命名的时候,会重新创建一个’tom’和“jack”对象,替换旧的’name’对象。这就说明了,浅复制(shallow copy),它复制了对象,但对于对象中的元素,依然使用引用.
下面试试复制的对象中包含可变对象:
上面的示例中,复制后的clis的修改影响到了原来的lis,这并不是我们想要的。如果希望复制一个容器对象,以及它里面的所有元素(包含元素的子元素),使用copy.deepcopy,这个方法会消耗一些时间和空间,不过,如果你需要完全复制,这是唯一的方法.
注意:
1、对于非容器类型(如数字、字符串、和其他‘原子’类型的对象)没有被拷贝一说。
2、如果元祖变量只包含原子类型对象,则不能深copy。
参考:http://www.cnblogs.com/BeginMan/p/3197649.html
一、熟悉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.cnblogs.com/BeginMan/p/3197649.html
相关文章推荐
- Python 拷贝对象(深拷贝deepcopy与浅拷贝copy)
- python对象的浅拷贝和深拷贝
- Python 拷贝对象(深拷贝deepcopy与浅拷贝copy)
- python中的对象拷贝示例 python引用传递
- Python 对象的深拷贝与浅拷贝 -- (转)
- Python Cookbook 4.1 复制(拷贝)对象(浅复制和深复制)
- Python 拷贝对象(深拷贝deepcopy与浅拷贝copy)
- 浅拷贝深拷贝Python对象的拷贝
- Python对象的拷贝
- Python 关于拷贝(copy)汇总(列表拷贝 // 字典拷贝 // 自定义对象拷贝)
- [转]python 复制(拷贝)对象
- python 引用,拷贝,对象回收,弱引用
- Python 拷贝对象(深拷贝deepcopy与浅拷贝)
- 拷贝Python 对象-----浅拷贝和深拷贝
- Python 初体验之 对象的引用和拷贝
- Python 拷贝对象(深拷贝deepcopy与浅拷贝copy)
- python技巧-对象拷贝
- Python Cookbook 4.1 复制(拷贝)对象(浅复制和深复制)
- 谈谈Python中对象拷贝
- 004_001 Python 对象拷贝