Python深拷贝和浅拷贝

1- Python引用计数[1]

  1.1 引用计数机制

    引用计数是计算机编程语言中的一种内存管理技术,是指将资源(可以是对象、内存或磁盘空间等等)的被引用次数保存起来,当被引用次数变为零时就将其释放的过程。使用引用计数技术可以实现自动资源管理的目的。同时引用计数还可以指使用引用计数技术回收未使用资源的垃圾回收算法。

    当创建一个对象的实例并在堆上申请内存时,对象的引用计数就为1,在其他对象中需要持有这个对象时,就需要把该对象的引用计数加1,需要释放一个对象时,就将该对象的引用计数减1,直至对象的引用计数为0,对象的内存会被立刻释放。

  1.2 垃圾回收

    当对象的引用计数为0,对象的内存会被立刻释放,称为垃圾回收

  1.3 引用计数增加情况

    (1). 对象被创建:x=4

    (2). 另外的别人被创建:y=x

    (3). 被作为参数传递给函数:foo(x)

    (4). 作为容器对象的一个元素:a=[1,x,'33']

  1.4 引用计数减少情况

    (1). 一个本地引用离开了它的作用域。比如上面的foo(x)函数结束时,x指向的对象引用减1。

    (2). 对象的别名被显式的销毁:del x ;或者del y

    (3). 对象的一个别名被赋值给其他对象:x=789

    (4). 对象从一个窗口对象中移除:myList.remove(x)

    (5). 窗口对象本身被销毁:del myList,或者窗口对象本身离开了作用域。


 2- 深浅拷贝[2,3]

  2.1 赋值    

    其实python中的赋值其实是使引用计数+1,例如:

foo1 = 1.0
foo2 = foo1
foo1 is foo2   #True
id(foo1) = 18719720
id(foo2) = 18719720

    但是如果是这样:

foo1=1.0
foo2=1.0
foo1 is foo2    #False
id(foo1) = 18719888
id(foo2) = 18719840

    这时你会发现,这其实是创建了两个不同的对象,用内建函数id()可以发现,二者的身份不同。

    其实python还有一个特例,例如:

a = 1
b = 1
id(a) = 14332248
id(b) = 14332248

    原因是python认为这些小整型是会经常用到的,所以python会缓存一部分小整型。

    

  2.2 深拷贝 & 浅拷贝

    序列类型的可以通过三种方式实现浅拷贝,浅拷贝也是默认的拷贝类型:(1)完全切片操作;(2)利用工厂函数,比如list()等;(3)使用copy模块中的copy()函数。

    在《Python核心编程》一书中说道,“对一个对象进行浅拷贝其实是新创建了一个类型跟原对象一样,其内容是原来对象元素的引用,换句话说,这个拷贝的对象本身是新的,但是它的内容不是”。

import copy
a = [1, 2, 3, 4, ['a', 'b', 'c']]
b = a
c = copy.copy(a)

id(a)       #139879301469392
id(b)       #139879301469392        
id(c)       #139879298646816, 可以看出所谓的“拷贝对象本身是新的“

[id(x) for x in a]        #[14332248, 14332224, 14332200, 14332176, 139879298781912]
[id(x) for x in b]        #[14332248, 14332224, 14332200, 14332176, 139879298781912]
[id(x) for x in c]        #[14332248, 14332224, 14332200, 14332176, 139879298781912], 即”内容是旧的“

  最后,深拷贝和浅拷贝的区别^_^

import copy
a = [1, 2, 3, 4, ['a', 'b', 'c']]
c = copy.copy(a)
d = copy.deepcopy(a)

id(a)       #139879301469392  
id(c)       #139879298646816
id(d)       #139879301462784

[id(x) for x in a]        #[14332248, 14332224, 14332200, 14332176, 139879298781912]
[id(x) for x in c]        #[14332248, 14332224, 14332200, 14332176, 139879298781912]
[id(x) for x in d]        #[14332248, 14332224, 14332200, 14332176, 139879302072512]

##################################
a.append(5)
a[4].append('hello')

a        #[1, 2, 3, 4, ['a', 'b', 'c', 'hello'], 5]
c        #[1, 2, 3, 4, ['a', 'b', 'c', 'hello']]
d        #[1, 2, 3, 4, ['a', 'b', 'c']]

[id(x) for x in a]        #[14332248, 14332224, 14332200, 14332176, 139879298781912, 14332152]
[id(x) for x in c]        #[14332248, 14332224, 14332200, 14332176, 139879298781912]
[id(x) for x in d]        #[14332248, 14332224, 14332200, 14332176, 139879302072512]

#################################
a[1] = 0
c[2] = 0
d[3] = 0

a        #[1, 0, 3, 4, ['a', 'b', 'c', 'hello'], 5]
c        #[1, 2, 0, 4, ['a', 'b', 'c', 'hello']]
d        #[1, 2, 3, 0, ['a', 'b', 'c']]

[id(x) for x in a]        #[14332248, 14332272, 14332200, 14332176, 139879298781912, 14332152]  a[1]关联到新对象

[id(x) for x in c]        #[14332248, 14332224, 14332272, 14332176, 139879298781912]  c[2]关联到新对象

[id(x) for x in d]        #[14332248, 14332224, 14332200, 14332272, 139879302072512]  d[3]关联到新对象
################################
# 1
del a[0]
a        #[   0, 3, 4, ['a', 'b', 'c', 'hello'], 5]
c        #[1, 2, 0, 4, ['a', 'b', 'c', 'hello']]
d        #[1, 2, 3, 0, ['a', 'b', 'c']]

del c[0]
a        #[   0, 3, 4, ['a', 'b', 'c', 'hello'], 5]
c        #[   2, 0, 4, ['a', 'b', 'c', 'hello']]
d        #[1, 2, 3, 0, ['a', 'b', 'c']]

del d[0]
a        #[   0, 3, 4, ['a', 'b', 'c', 'hello'], 5]
c        #[   2, 0, 4, ['a', 'b', 'c', 'hello']]
d        #[   2, 3, 0, ['a', 'b', 'c']]

# 2
del a[3][0]
a        #[   0, 3, 4, [     'b', 'c', 'hello'], 5]
c        #[   2, 0, 4, [     'b', 'c', 'hello']]
d        #[   2, 3, 0, ['a', 'b', 'c']]

简单点说

  1. copy.copy 浅拷贝     -拷贝一个对象,但是对象的属性还是引用原来的。拷贝了一个不可变的对象的引用, 修改你得到的变量只会让该变量的引用指向一个新的对象(如a或c中数值元素改变不会影响另一个)

               可以这样记忆,浅拷贝的各个元素整体上改变是没有影响的,但仅元素部分修改是互相牵制的

a = [1, 2, ['a', 'b'], ['c', 'd']]
b = copy.copy(a)

#整体
a[0] = 0
a    #[0, 2, ['a', 'b'], ['c', 'd']]
b    #[1, 2, ['a', 'b'], ['c', 'd']]

del a[3]
a    #[0, 2, ['a', 'b']]
a    #[1, 2, ['a', 'b'], ['c', 'd']]

#局部
a[2].append('haha')
a    #[0, 2, ['a', 'b', 'haha']]
a    #[1, 2, ['a', 'b', 'haha'], ['c', 'd']]


  2. copy.deepcopy 深拷贝  -拷贝一个对象,把对象里面的属性也做了拷贝,deepcopy之后完全是另一个对象了,实现完全独立

#打印['a','b','c']地址
[id(x) for x in a[3]]    #[1398793470232, 139879343471752, 139879314380720]
[id(x) for x in c[3]]    #[1398793470232, 139879343471752, 139879314380720]
[id(x) for x in d[3]]    #[1398793470192, 139879343470232, 139879343471752]

[1] Python是如何进行内存管理的?

[2] Python的赋值、浅拷贝和深拷贝

[3] 从Python中copy与deepcopy的区别看Python引用

原文地址:https://www.cnblogs.com/freyr/p/4410281.html