【Python学习笔记】 第6章 动态类型
缺少声明语句的情况
我们没有声明变量的类型,但是当直接赋值时,Python知道应该把它转换为什么类型。(如a = 3
,Python把它识别为整数)
变量,对象和引用
Python这么解释变量:
-
变量创建:第一次给变量赋值时相当于创建变量,之后的赋值改变变量值。
-
变量类型:变量不会拥有任何和它关联的类型信息和约束,它只是引用了一个特定的对象而已。
-
变量使用:当变量出现在表达式中,它会被使用的对象代替。
Python执行赋值语句(以a = 3
为例)的步骤:
-
创建一个对象表示值3
-
如果第一次对
a
赋值,创建一个变量a
-
将变量
a
与对象3连接
实际效果如图所示:
具体而言,
- 变量是一个系统表的入口,包含了指向对象的连接。
- 在Python中,从变量到对象的连接被称为引用。
- 对象被分配到的一块内存,有足够的空间表示它们所代表的值。
- 引用是自动形成的从变量到对象的指针。
对象包含更复杂的信息,其中类型标志符标识这个对象的类型;引用的计数器决定何时收回对象。
类型属于对象,而不是变量
以下代码是可行的:
>>> a = 3
>>> a = 'spam'
>>> a = 1.23
这是因为,在Python中,变量没有类型。类型属于对象而不是变量名。因此,上述操作只是让变量引用了不同类型的对象而已。
但是,每个对象包含一个头部信息,标记了这个对象的类型。
因此,Python中的类型与对象相关联,而不是与变量关联。
对象的垃圾收集
如果原来的对象没有被引用的话,那么这个对象占用的空间被回收。在上述代码中,执行a = 'spam
语句时,对象3
没有被引用,因此它的空间被回收。
垃圾收集的工作原理是:Python为每个对象保留一个计数器,记录当前指向该对象的引用的数目。一旦这个计数器被设置为0,那么它的内存空间就会被回收。
在编写Python代码时,我们不需要手动实现垃圾收集。
关于Python垃圾回收的更多讨论
由于引用实现为指针,一个对象可能会引用自身,或者引用另一个引用了自身的对象。例:
>>> L = [1, 2]
>>> L.append(L)
>>> L
[1, 2, [...]]
这里L
的第三个元素是指向自身的引用,因此它的计数器永远不会被清零。
共享引用
在交互式命令行下输入:
>>> a = 3
>>> b = a
结果如图所示:
这种情况被称为共享引用,即多个变量名引用同一个对象。
在此基础上,输入:
>>> a = 'spam'
它创建一个新对象(而不是改变对象3
),并设置a
对这个新对象进行引用,而b
指向的值不会改变。
执行下面语句的时候a
指向新的对象,而不是原来的、被修改过的对象。这是因为,整数是不可变的,不能在原位置修改它。
>>> a = 3
>>> b = a
>>> a = a + 2
>>> a, b
(5, 3)
共享引用和在原位置修改
有一些对象和操作可以在原位置改变对象。因此,对应这种类型的对象,共享引用时需要小心,因为对一个变量名的修改会影响其他变量。
如果给L1
改变其引用对象的一个元素,由于这个对象与其他对象共享,那么这种修改也会影响程序的其他部分。
要避免这种情况,我们可以请求Python复制对象,而不是创建一样的引用。对于列表而言,分片操作就是创建一个新的变量:
>>> L1 = [2, 3, 4]
>>> L2 = L1[:]
>>> L1[0] = 24
>>> L1
[24, 3, 4]
>>> L2
[2, 3, 4]
此时L1
、L2
指向不同的内存区域。对于其他核心类型(字典、集合),复制的方法是:使用X.copy()
方法,或者将原有的对象传入它们的类型构造函数中,或者使用标准库的copy
模块。其中copy
模块提供了两种复制方式:深复制deepcopy
和浅复制copy
。
共享引用和相等
在实际应用中,Python不一定回收对象,而是保存下来以便下一次重复利用。
基于Python的引用模型,我们有两种方法检查是否相等:
>>> L1 = [1, 2, 3]
>>> L2 = L1
>>> L3 = [1, 2, 3]
>>> L1 == L2, L1 == L3
(True, True)
>>> L1 is L2, L1 is L3
(True, False)
这里,==
检查值是否相等;is
检查对象的同一性(是否引用同一个对象)。如果值相等,但不是同一个值,那么is
判定的返回值为False
。
但是,小的整数和字符串会被缓存并复用(而不是创建一个新的对象):
>>> X = 42
>>> Y = 42
>>> X == Y, X is Y
(True, True)
我们可以通过sys
模块中的getrefcount
返回对象的引用次数。
>>> import sys
>>> sys.getrefcount(1)
1000000207
动态类型随处可见
在Python中,任何东西看起来是通过赋值和引用工作的,而动态类型是Python的唯一的赋值模型。