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,本文内容只作为学习过程的一个总结和理解的记录。其中不乏有用词不当的地方。有些地方也是自己实验过后得出的总结,可能还不够全面。如果您觉得我的理解有错误和偏差,希望您能够在留言中指出来,我会继续去探究印证。谢谢啦!
====================更新===============2013.9.24
1、python支持多继承
 
 
对于多继承中的缺点python是这么解决的:
 
 
设SubDemo继承了Demo1 和 Demo2,Demo1和Demo2中都拥有函数fun()但是内部实现不同。伪代码如下:
 
class Demo1():
.......
def fun(self):
print "Demo1's fun()"
.......
 
class Demo2():
.......
def fun(self):
print "Demo2's fun()"
....... 
 
SubDemo声明如下:
class SubDemo(Demo1,Demo2):
pass
 
 
当创建一个SubDemo的实例后调用fun方法,返回的结果是:Demo1's fun()
 
调换Demo1和Demo2位置再次重复以上步骤,返回的结果是:Demo2's fun()
 
 
以上实验说明,python会根据继承的父类在参数列表中的位置来决定调用哪一个父类中的重复函数。
 
 
2、python中所有对付类的操作都需要显示的执行

相关内容

    暂无相关文章

评论关闭