主题是变量和内存管理的细节, 包括:
???? 变量无须事先声明
???? 变量无须指定类型
???? 程序员不用关心内存管理
???? 变量名会被“回收”
???? del 语句能够直接释放资源
3.5.1 变量定义
大多数编译型语言,变量在使用前必须先声明,其中的 C 语言更加苛刻:变量声明必须位
于代码块最开始,且在任何其他语句之前。其它语言,像C++和Java,允许“随时随地”声明
变量,比如,变量声明可以在代码块的中间,不过仍然必须在变量被使用前声明变量的名字和
类型。在Python 中,无需此类显式变量声明语句,变量在第一次被赋值时自动声明。和其他大
多数语言一样,变量只有被创建和赋值后才能被使用。
>>> a
Traceback (innermost last):
File "", line 1, in ?
NameError: a
Edit By Vheavens
Edit By Vheavens
变量一旦被赋值,您就可以通过变量名来访问它。
>>> x = 4
>>> y = 'this is a string'
>>> x
4
>>> y
'this is a string'
3.5.2 动态类型
还要注意一点,Python 中不但变量名无需事先声明,而且也无需类型声明。Python 语言中,
对象的类型和内存占用都是运行时确定的。尽管代码被编译成字节码,Python 仍然是一种解释
型语言。在创建--也就是赋值时,解释器会根据语法和右侧的操作数来决定新对象的类型。
在对象创建后,一个该对象的应用会被赋值给左侧的变量。
3.5.3 内存分配
作为一个负责任的程序员,我们知道在为变量分配内存时,是在借用系统资源,在用完之
后, 应该释放借用的系统资源。Python 解释器承担了内存管理的复杂任务, 这大大简化了应
用程序的编写。你只需要关心你要解决的问题,至于底层的事情放心交给Python 解释器去做就
行了。
3.5.4 引用计数
要保持追踪内存中的对象, Python 使用了引用计数这一简单技术。也就是说Python 内部
记录着所有使用中的对象各有多少引用。你可以将它想像成扑克牌游戏“黑杰克”或“21 点”。
一个内部跟踪变量,称为一个引用计数器。至于每个对象各有多少个引用, 简称引用计数。当
对象被创建时, 就创建了一个引用计数, 当这个对象不再需要时, 也就是说, 这个对象的
引用计数变为0 时, 它被垃圾回收。(严格来说这不是100%正确,不过现阶段你可以就这么
认为)
增加引用计数
Edit By Vheavens
Edit By Vheavens
当对象被创建并(将其引用)赋值给变量时,该对象的引用计数就被设置为1。
当同一个对象(的引用)又被赋值给其它变量时,或作为参数传递给函数, 方法或类实例
时, 或者被赋值为一个窗口对象的成员时,该对象的一个新的引用,或者称作别名,就被创建
(则该对象的引用计数自动加1)。
图3–2 有两个引用的同一对象
请看以下声明:
x = 3.14
y = x
语句 x=3.14 创建了一个浮点数对象并将其引用赋值给 x。 x 是第一个引用, 因此,该
对象的引用计数被设置为1。语句 y=x 创建了一个指向同一对象的别名 y(参阅图3-2)。事
实上并没有为Y 创建一个新对象, 而是该对象的引用计数增加了1 次(变成了2)。这是对象
引用计数增加的方式之一。还有一些其它的方式也能增加对象的引用计数, 比如该对象作为参
数被函数调用或这个对象被加入到某个容器对象当中时。
总之,对象的引用计数在
???? 对象被创建
x = 3.14
???? 或另外的别名被创建
y = x
???? 或被作为参数传递给函数(新的本地引用)
foobar(x)
Edit By Vheavens
Edit By Vheavens
???? 或成为容器对象的一个元素
myList = [123, x, 'xyz']
下面让我们来看一下引用计数是如何变少的。
减少引用计数
当对象的引用被销毁时,引用计数会减小。最明显的例子就是当引用离开其作用范围时,
这种情况最经常出现在函数运行结束时,所有局部变量都被自动销毁,对象的引用计数也就随
之减少。
当变量被赋值给另外一个对象时,原对象的引用计数也会自动减1:
foo = 'xyz'
bar = foo
foo = 123
当字符串对象"xyz"被创建并赋值给foo 时, 它的引用计数是1. 当增加了一个别名 bar
时, 引用计数变成了2. 不过当foo 被重新赋值给整数对象123 时, xyz 对象的引用计数自
动减1,又重新变成了1.
其它造成对象的引用计数减少的方式包括使用 del 语句删除一个变量(参阅下一节), 或
者当一个对象被移出一个窗口对象时(或该容器对象本身的引用计数变成了0 时)。 总结一下,
一个对象的引用计数在以下情况会减少:
???? 一个本地引用离开了其作用范围。比如 foobar()(参见上一下例子)函数结束时。
???? 对象的别名被显式的销毁。
del y # or del x
???? 对象的一个别名被赋值给其它的对象
x = 123
???? 对象被从一个窗口对象中移除
myList.remove(x)
???? 窗口对象本身被销毁
del myList # or goes out-of-scope
Edit By Vheavens
Edit By Vheavens
参阅11.8 了解更多变量作用范围的信息。
del 语句
Del 语句会删除对象的一个引用,它的语法是:
del obj1[, obj2[,... objN]]
例如,在上例中执行del y 会产生两个结果:
???? 从现在的名字空间中删除 y
???? x 的引用计数减一
引申一步, 执行 del x 会删除该对象的最后一个引用, 也就是该对象的引用计数会减为
0, 这会导致该对象从此“无法访问”或“无法抵达”。 从此刻起, 该对象就成为垃圾回收
机制的回收对象。 注意任何追踪或调试程序会给一个对象增加一个额外的引用, 这会推迟该
对象被回收的时间。
3.5.5 垃圾收集
不再被使用的内存会被一种称为垃圾收集的机制释放。象上面说的, 虽然解释器跟踪对象
的引用计数, 但垃圾收集器负责释放内存。垃圾收集器是一块独立代码, 它用来寻找引用计
数为0 的对象。它也负责检查那些虽然引用计数大于0 但也应该被销毁的对象。 特定情形会导
致循环引用。
一个循环引用发生在当你有至少两个对象互相引用时, 也就是说所有的引用都消失时, 这
些引用仍然存在, 这说明只靠引用计数是不够的。Python 的垃圾收集器实际上是一个引用计
数器和一个循环垃圾收集器。 当一个对象的引用计数变为0,解释器会暂停,释放掉这个对象
和仅有这个对象可访问(可到达)的其它对象。作为引用计数的补充, 垃圾收集器也会留心被
分配的总量很大(及未通过引用计数销毁的那些)的对象。 在这种情况下, 解释器会暂停下
来, 试图清理所有未引用的循环。
阅读(1087) | 评论(0) | 转发(0) |