Python 学习手册——第六章 动态类型简介
Python语句在使用变量时,没有声明变量的存在以及它的类型。
一、变量、对象、引用
a = 3
对于上面语句可以分成三个步骤分析:
- 创建一个对象代表值 3
- 创建一个变量 a(假设它还没有创建过)
- 将变量 a与对象 3建立引用关系
实际效果如下图:
a 实际上是对象 3 的一个引用
而且深入来说,每个对象都有两个标准的头部信息:一个类型标志符取标识这个对象的类型,以及一个引用的计数器,来决定是不是可以回收这个对象。
二、类型属于对象的属性,而不是变量。
a = 3 a = "yang" a = 1.22
上面的代码在 c 中是不可行的,但在这里是可以的,为什么呢?
很简单, 变量名没有类型,类型只属于对象,而不是变量名。我们只是把该变量名修改为对其它类型对象的引用。也如上所说,每个对象都保留了自己的一个头部信息来标记自己的类型(是整数还是字符串)。
那么引用计数器是干嘛呢?
我们不妨考虑一下在给变量 a 赋予了一个新的对象 “yang” 之后,之前的对象 3 发生了什么?实际上之前的对象所占用的空间会被回收,除非它再次被别的变量名或者对象引用。这种自动回收对象空间的技术叫做垃圾自动回收机制。而计数器承担了记录当前指向该对象的引用的数目的功能,一旦计数器值为零,这个对象的空间就会自动被回收。
这意味着我们可以任意使用对象而不需要考虑释放内存空间,与 c 这样的底层语言相比,省略了不少功夫。
三、共享引用
现在我们考虑一下多个变量同时引用同一个对象的情况。
a = 3
b = a
上面语句的结果就是使得变量 a,b 引用了同一个对象,在python 中叫共享引用。
与 c 语言不同的是如果我们改变了变量 a 的引用对象,并不会改变变量 b 的值,而是仅仅改变了前者的值。
四、共享引用和原处修改
在共享引用的时候,如果对于支持在原处进行修改而不是生成一个新的对象,一定要格外注意:对一个变量名的修改会通过改变引用对象值而影响引用同样对象的其它变量。
#以列表为例说明,列表支持在原处修改对象而不创建一个新的对象 list_1 = [1, 2, "yang"] list_2 = list_1 list_1[0] = 3 # 在原处修改对象 print(list_1) #[3, 2, 'yang'] print(list_2) #[3, 2, 'yang']
如果我们不希望这样的现象发生,需要Python拷贝对象,而不是创建引用。
Python中有很多拷贝对象的方法,包括内置的列表函数以及标准的 copy 模块。这里介绍常用的从头到尾分片列表的拷贝方法:
list_1 = [1, 2, "yang"] list_2 = list_1[:] #从头到尾的分片 list_1[0] = 3 # 在原处修改对象 print(list_1) #[3, 2, 'yang'] print(list_2) #[1, 2, 'yang']
这里对 list_1 的更改不会影响 list_2 ,因为 list_2 引用的是 list_1 引用对象的一个拷贝.两个变量指向的是不同的内存区域。
关于拷贝还有深拷贝与浅拷贝的区别。
五、共享引用和相等
比较两种引用对象是否相同有两种方法: “==” 和 “is”
前者是比较两个对象是否有相同的值,后者比较的是两个引用是否是同一个对象(同一块内存空间)。从下面代码可以看出。
#共享引用 list_1 = [1, 2, "yang"] list_1_0=list_1 print(list_1 == list_1_0) #True print(list_1 is list_1_0) #True #创建一个新的对象 list_1_1= [1, 2, "yang"] print(list_1 == list_1_1) #True print(list_1 is list_1_1) #False #创建一个拷贝 list_1_2 = list_1[:] print(list_1 == list_1_2) #True print(list_1 is list_1_2) #False