标签:
作为一门动态语言,python很重要的一个概念就是动态类型,即对象的类型和内存占用都是运行时确定的。(Why?)运行时,解释器会根据语法和右操作数来决定新对象的类型。
动态类型的实现,是通过引用和对象的分离达到的。对象是存放在内存中的数据实体,而引用(reference)可以理解成一个封装好的指向对象的指针。不过操作更加方便和安全。就像C++中的引用是对指针操作的简化和封装一样。在python中,内存的管理,即分配与回收,都是由python解释器来做的,程序员不需要关心太多。或者,也可以把引用理解成是对象的一个别名,一个对象可以有多个别名都指向它。比如后面的代码中的3.14这个浮点对象,x 和 y 都是指向它这个对象的,都可以看做是它的别名。就像一个人可以大名叫张某某,而小名叫张三一样。
在python中,创建一个对象时,解释器负责内存的分配,同时内部一个叫做引用计数器的东西在对象被赋值给变量时,置为1。当把对象赋值给变量时,是把对象的引用赋给了变量。当再次将这个对象赋值给其他变量时,引用计数器+1。如:
x = 3.14 y = x
第一行代码中,创建了一个浮点型的对象,同时,将其引用赋值给了一个名为x的变量。这是引用计数器的值为1。接下来,将 x 赋值给 y ,即再次将这个浮点对象的引用赋值给y,同时引用计数器+1, 值为2。也就是说,当把x赋值给 y 是并没有创建新的对象。内存中的数据对象依旧只有一个,但是有两个变量x , y 都指向这同一个对象。这一点,和C/C++是完全不一样的。但是呢,就像前文中说的,和C/C++中的指针有点像。如:
int tmp = 10; int *p = &tmp;
上述代码中,先定义了一个int型变量tmp,并初始化为10,接下来,定义了一个指针p指向tmp对象的内存。也就是说,tmp和*p指向的是同一个内存对象。不过,python做了底层的工作,我们只需要像操作普通变量那样操作就可以得到类似指针的效果了。
回到python,当我改变 x,y 中的 其中一个的值时,另一个是不受影响的。如:
x = 3.14 y = x x = “A”
这时,如果进行输出,x 的值为5, 而 y 的值依旧为 3.14 。因为当执行x = “A” 语句时,其实是创建了一个新的对象,并把其引用赋值给了 x 。这时,x 和 y 已经是两个不相关的两个变量了。因为它们指向了不同的对象。在执行这个语句的同时,y 所执行的 3.14这个浮点数对象的引用计数器减一。当引用计数器的值为零,即没有变量指向这个对象时,系统会自动销毁这个对象并回收内存。(当然,实现上来说,并不一定是引用计数器为0了,就马上回收,可能会有一些回收的策略。) 此外,可以看到的是,一开始 x 指向的是一个浮点型对象,而现在却指向了一个字符串。因为在 Python 中,变量名仅仅与是指向对象,而与对象的类型是无关的。这里涉及到弱类型,强类型,就不涉及更多了,因为不太懂。(Right?)
回到引用计数,在以下4种情况下,引用计数器增加:
1> 对象被创建
x = 3.14
2> 或另外的别名被创建
y = x
3> 或作为参数传递给函数(新的本地引用)
foobar(x)
4> 或称为容器对象的一个元素 (容器?)
myList = [123, x, ‘xyz’]
而在以下情况下,引用计数器减少:
1> 一个本地引用离开了其作用范围,比如foobar()函数结束时。
2> 对象的别名被显示销毁
del y
3> 对象的别名被赋给其他对象
x = 123
4> 对象从窗口对象中移除
myList.remove(x)
5> 窗口对象本身被销毁
del myList
del 语句会删除对象的一个引用。它有两个效果:
1> 从现在的名称空间中删除变量名
2> 对象的引用计数-1
转载请注明地址: http://www.qyspaces.com/?p=272
标签:
原文地址:http://www.cnblogs.com/qiuyi116/p/4461416.html