Python:动态类型简介
下一节:python字符串
1. 缺少类型声明语句的情况
在 Python 中,类型是在运行过程中自动决定的,而不是通过代码声明。
1.1 变量、对象和引用
- 变量创建:一个变量,就像 a,当代码第一次给它赋值时就创建了它。之后的赋值将会改变已创建的变量名的值。
- 变量类型:变量永远不会有任何和它关联的类型信息或约束。类型的概念是存在于对象中而不是变量名中。
- 变量使用:当变量出现在表达式中时,它马上被当前引用的对象所代替,无论这个对象是什么类型。所有的变量必须在其使用前明确地赋值,使用未赋值的变量会产生错误。
从概念上说,对 a = 3,Python 将会执行三个不同的步骤去完成请求:
- 创建一个对象来代表值 3。
- 创建一个变量 a,如果它还没有创建的话。
- 将变量与新的对象 3 相连接。
变量和对象保存在内存中的不同部分,并通过连接相关联。
Python 中从变量到对象的连接称为引用。引用是一种关系,以内存中的指针的形式实现。
- 变量是一个系统表的元素,拥有指向对象的连接的空间。
- 对象是分配的一块内存,有足够的空间去表示它们所代表的的值。
- 引用是自动形成的从变量到对象的指针。
1.2 类型属于对象,而不是变量
在 Python 中,变量名没有类型,类型属于对象,而不是变量名。Python 的变量就是在特定的时间引用了一个特定的对象。
1 | a = 3 # 整数 |
1 | a = 'spam' # 现在是一个字符串 |
1 | a = 1.23 # 现在是一个浮点数 |
每个对象都包含了一个头部信息,其中标记了这个对象的类型。
1.3 对象的垃圾收集
每当一个变量名被赋予了一个新的对象,之前的那个对象占用的空间就会被回收(如果它没有被其他的变量名或对象所引用的话)。这种自动回收对象空间的技术叫做垃圾回收。
在内部,Python 在每个对象中保持了一个计数器,计数器记录了当前指向该对象的引用的数目。一旦(并精确在同一时间)这个计数器被设置为零,这个对象的内存空间就会自动回收。
2. 共享引用
1 | a = 3 |
赋值语句 b = a 之后,变量 b 成为对象 3 的一个引用,即 a 和 b 都引用了相同的对象。这叫做共享引用。
1 | a = 3 |
3
对于上例,简单地创建了一个新的对象(‘spam’),并设置 a 对这个新的对象进行引用。这并不会改变 b 的值,b 仍然引用原始的对象。
给一个变量赋一个新的值,并不是替换了原始的对象,而是让这个变量去引用完全不同的一个对象。当可变对象以及原处改变进入这个场景,那么这个情形会有某种改变。
2.1 共享引用和在原处修改
对于支持原处修改的对象,共享引用时需要加倍小心,因为对一个变量名的修改会影响其他的变量。
例如列表支持对位置的在原处赋值:
1 | L1 = [2, 3, 4] |
(24, [2, 3, 4])
L1 直接设置为一个不同的对象,L2 仍是引用最初的列表。
1 | L1 = [2, 3, 4] # 可变对象 |
([24, 3, 4], [24, 3, 4])
这里改变了 L1 所引用的对象的一个元素。这类修改会覆盖列表对象中的某部分。
因为这个列表对象是与其他对象共享的,那么一个像这样在原处的改变不仅仅会对 L1 有影响。
这种行为仅在支持原处修改的可变对象上发生,如果不想要这样的现象发生,可以拷贝对象,而不是创建引用。
1 | L1 = [2, 3, 4] |
([24, 3, 4], [2, 3, 4])
这种分片技术不会应用在其他的可变的核心类型(字典和集合)上,复制一个字典或集合应该使用 X.copy()
方法调用。
1 | import copy |
2.2 共享引用和相等
在 Python 中有两种不同的方法去检查是否相等:
1 | L = [1, 2, 3] |
True
1 | L is M # 相同对象 |
True
“== 操作符”,测试两个被引用的对象是否具有相同的值。
“is 操作符”,检查对象的同一性,是否精确地指向同一个对象。可以是检测共享引用的一种办法。
1 | L = [1, 2, 3] |
True
1 | L is M # 不同的对象 |
False
1 | X = 42 |
True
1 | X is Y |
True
因为小的整数和字符串被缓存并复用,所以 X 和 Y 引用了一个相同的对象。