Python之探究类的复制和垃圾回收
一、引用计数和垃圾回收
先看一段代码:
[python
In [43]: a = 34
In [44]: b = a
In [45]: del a
In [46]: b
Out[46]: 34
In [47]: a
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
/home/kehr/<ipython-input-47-60b725f10c9c> in <module>()
----> 1 a
NameError: name 'a' is not defined
就以上这段代码,说说我的理解。
由于,a和b是指向同一块内存的引用,那么,修改a的值b的值一定会改变。
亮点在 “del a” 这一句,执行这句代码,销毁的是a这个引用变量呢,还是a指向的那块内存呢?或者是二者都被销毁了呢?
那就打印一下b和a
可以看见b和它指向的那块内存并没有消失,而b又是和a指向同一块内存。所以 del a 只是销毁了a这个引用变量
打印一下a,就会出现 NameError 的错误提示。
小结一下: del 只会销毁内存的引用
那么还有点问题,是不是由于数字和字符串这些是共享常量才会出现这种结果呢?我们再来测试一下一个全新的对象试一试
[python]
In [49]: a = [1,2,3]
In [50]: b = a
In [51]: del a
In [52]: b
Out[52]: [1, 2, 3]
In [53]: a
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
/home/kehr/<ipython-input-53-60b725f10c9c> in <module>()
----> 1 a
NameError: name 'a' is not defined
可以看到,结果是一样的。那么我是不是可以下一个结论:del 语句只会销毁对象的引用变量,而对对象没有影响。
这里的对象指的是内存中的一块空间。
那么,如果 a = [“zhangsan”,"lisi"] 我们销毁了a,对象["zhangsan","lisi"]去哪里了呢?会不会内存泄露呢?
其实我们不必担心,python的垃圾回收机制,会帮我们解决这一问题。这里我们就需要了解一下对象的引用计数了
每一个对象被创建后,它的引用计数就会加一,当有其它变量再次引用该对象的时候,引用计数再次加一。如果删除这个对象的一个引用,它的引用计数就会减一。当一个对象的引用计数变为零的时候,就会被当做垃圾,被处理掉。
二、引用与复制
先来看这几条条语句:
[python
In [2]: a = [1,2,3]
In [3]: b = a
In [4]: import copy
In [5]: c = list(a)
In [6]: d = copy.deepcopy(a)
In [7]: b
Out[7]: [1, 2, 3]
In [8]: c
Out[8]: [1, 2, 3]
In [9]: d
Out[9]: [1, 2, 3]
看起来 b,c,d 都成功的把a的值“复制”过来了,但是他们是不同的。不同在哪里呢?我们知道,复制,是把一个变量的值copy一份到另外一个变量中,两个变量相互独立,对其中一个变量值的修改并不影响另一个变量的值。以上这段代码的做法体现的不同之处就在这里。
下面这几段代码,先看看效果:
[python]
In [7]: a
Out[7]: [1, 2, 3]
In [8]: b
Out[8]: [1, 2, 3]
In [9]: c
Out[9]: [1, 2, 3]
In [10]: d
Out[10]: [1, 2, 3]
b = a, c = list(a), d = copy.deepcopy(a)
[python
In [14]: a
Out[14]: [1, 2, 3]
In [15]: a.append([45,34])
In [16]: a
Out[16]: [1, 2, 3, [45, 34]]
In [17]: b
Out[17]: [1, 2, 3, [45, 34]]
In [18]: c
Out[18]: [1, 2, 3]
In [19]: d
Out[19]: [1, 2, 3]
可以看到a改变之后,只有b变了,c和d是不是真的完成了复制呢?
[python
In [32]: a
Out[32]: [1, 2, 3, [45, 34]]
In [33]: c = list(a)
In [34]: d = copy.deepcopy(a)
In [35]: b
Out[35]: [1, 2, 3, [45, 34]]
In [36]: c
Out[36]: [1, 2, 3, [45, 34]]
In [37]: a[3][0] = 'zhangsan'
In [38]: a
Out[38]: [1, 2, 3, ['zhangsan', 34]]
In [39]: b
Out[39]: [1, 2, 3, ['zhangsan', 34]]
In [40]: c
Out[40]: [1, 2, 3, ['zhangsan', 34]]
In [41]: d
Out[41]: [1, 2, 3, [45, 34]]
到这里,我们能看出,当a改变,b,c也随之改变了,d依旧没变,那是不是说明只有d是成功的完成了复制,b和c都没有复制成功呢?这只对了一半,下面我们来一一说明。
第一句:b = a
可能用惯了C和Java对与复制这个概念,认为这么做就完成了复制。但是在python中这么做是不对的。我在python的书上看到这句话的解释是:“b是对a的引用”
对此,我有两种理解:
1、有一块内存地址存放着对象 [1,2,3] ,变量a作为这块内存的引用,存储了它的地址,变量b作为a的引用存储了a的地址
2。有一块内存地址存放着对象 [1,2,3] ,变量a和b作为这块内存的引用,都存储了它的地址。
比较这两个,我更倾向于第二种理解。
如此,修改a的值b的值也会被修改。
第二句:c = list(a)
这一句实际上是完成了对a的复制的,但是复制的不够彻底,这种复制被称为“浅复制”。为什么呢?
这样看a的内容了。如果a中没有包含对象(a = [1,2,3] c = list(a) ),那么c的这种复制是成功的,c的内容和a是各自独立的。复制的时候又创建了一块新的内存,存放a指向的那块内存的内容。
但是,如果a中包含对象(a = [1,2,3,[45,34]], c = list(a)),那么c的这种复制只成功了一半。首先,还是会创建一块内存,存放a对象中的内容,可是a对象中的内嵌对象是不会被复制过来的,他会由a和c共享。也就是说,除了内嵌脆响之外,其他的元素都是相互独立的,只有内嵌对象是被共享的。修改内嵌对象的值c和a的值都会改变,修改其它非对象元素,c和a的值只会改变一个。
第三句:d = copy.deepcopy(a)
这是为了解决第二句的漏洞而设计的。使用这一句才能够把a中的所有元素和对象完全复制。d和a是独立的,修改a中的元素和对象的值都不会影响d中的元素和对象的值。这种复制称为“深复制”
这三种复制,不能说哪一种更好,灵活的运用在需要他们的地方,才能体现出各自的价值和优越性。
初学python,本文内容只作为学习过程的一个总结和理解的记录。其中不乏有用词不当的地方。有些地方也是自己实验过后得出的总结,可能还不够全面。如果您觉得我的理解有错误和偏差,希望您能够在留言中指出来,我会继续去探究印证。谢谢啦
补充:Web开发 , Python ,