在python中,类型是在运行过程中自动决定的,而不是通过代码声明。
1 动态类型是python语言灵活的根源
变量创建:当代码第一次给变量赋值时就创建,之后的赋值将会改变已创建的变量名值。
变量类型:变量永远不会有任何和他关联的类型信息或约束。类型的概念是存在于对象中而不是变量名中,它是通用的,只是在一个特定的时间点,引用了一个特定的对象而已。
变量使用:所以的变量必须在其使用前明确赋值,使用未赋值的变量会产生错误。
python将会执行三个不同的步骤去完成下面的语句,1)创建一个对象来代表3;2)如果他还没有创建,创建一个变量a;3)将变量与新的对象相链接。
>>> a =3
在python中从变量到对象的连接称作引用,它以内存中的指针的形式实现。这里的变量是一个系统表的元素,拥有指向对象的连接空间,对象是分配的一块内存,有足够的空间去表示它们所代表的值;引用是自动形成的从变量到对象的指针。
至少从概念上讲,每一次运行一个表达式生成一个新的值,python都会创建一个新的对象。但从内部来看,python为了优化,缓存了不变对象并对其进行复用(一般是小的整数和小的字符串)。
对象都有两个标准的头部信息:一个类型标志符去标识这个对象的类型;一个引用的计数器,来决定是不是可以回收这个对象。 所以说类型属于对象,而不是变量名。
2 对象的垃圾回收
在python中,每当一个变量名被赋予一个新的对象,之前的那个对象占用的空间就会被回收(如果它没有被其他的变量名或对象引用的话。)这种自动回收对象空间的技术叫垃圾回收。
>>> a = 3
>>> a = 'span'
在内部,python是这样实现这个功能的:它在每一个对象中保持一个计数器,计数器记录了当前指向该对象的引用数目。一旦计数器被设置为0,这个对象的内存空间就会自动回收。这意味着在脚本中任意使用对象而不需要考虑释放内存空间。
注意:垃圾回收有一部分功能可以及时地检测并带有循环引用的对象,可以关闭这部分功能,但该功能是默认可用的
3 共享引用——多个变量名引用了同一个对象
>>> a = 3
>>> b =a
>>> a = 'span'
>>> b
3
a和b都引用对象3,即指向相同的内存空间。改变a的引用,b仍然引用原来的对象。可以看出赋值操作总是存储对象的引用。
1)不可变对象
>>> a = 3
>>> b =a
>>> a= a+2
>>> b
3
在python中,变量总是一个指向对象的指针,而不是可改变的内存区域的标签:给一个变量赋一个新值,并不是替换了原始对象,而是让这个变量去引用完全不同的一个对象。实际效果就是对一个变量赋值,仅仅影响那个被赋值的变量,这与C++效果是一致的。
注意:没有办法改变对象3的值
2)在原处修改的可变对象
>>> L1 = [2,3,4]
>>> L2 = L1
>>> L1[0] = 24
>>> L1
[24, 3, 4]
>>> L2
[24, 3, 4]
由于L1和L2共享列表对象,对L1引用对象的部分修改会影响变量L2。
如果您不想要这样的现象发生,需要python拷贝对象,而不是创建引用。拷贝一个列表的方法包括内置列表函数和标准库的copy模块以及最常用的方法是从头到尾分片。对字典的拷贝,还有字典的copy方法能够复制字典。
标准库中copy模块有一个通用的复制任意对象类型的调用copy,也有一个拷贝嵌套对象结构的调用deepcopy。
注意:重复能够增加层次深度,但同时也会带来嵌套引用的副作用。如果一个复合对象包含指向自身的引用,被叫做循环对象。
>>> L1 = [1,2,3]
>>> L2 = L1[:] # 对列表的拷贝
>>> L1[0]= 12
>>> L1
[12, 2, 3]
>>> L2
[1, 2, 3]
>>>
>>> D = {'a':1, 'b':2}
>>> B = D.copy() # 对字典的拷贝
>>> B
{'a': 1, 'b': 2}
>>>
>>> import copy
>>> X = [2,3,4]
>>> Y = copy.copy(X) # 任意对象的顶层拷贝
>>> Y
[2, 3, 4]
>>> Z = {'a':[1,2,3],'b':[4,5,6]}
>>> Y = copy.deepcopy(Z) # 嵌套拷贝
>>> Y
{'a': [1, 2, 3], 'b': [4, 5, 6]}
>>> L = [4,5,6]
>>> Y = [L]*3 # 嵌套引用带来副作用
>>> Y
[[4, 5, 6], [4, 5, 6], [4, 5, 6]]
>>> L[1] = 0
>>> Y
[[4, 0, 6], [4, 0, 6], [4, 0, 6]]
3) 对共享引用是否相等的判断与比较
因为python缓存并复用小的整数和小的字符串,这里对象42也许并不像我们所说的被回收,相反,它将可能仍被保存一个系统表中,等待下一次你的代码生成一个42来复用。当然,大多数类型的对象都会在不再引用时马上回收。
“==”操作符用于检测两个被引用的对象是否有相同的值,相等性比较。“is"操作符检测两个变量名是否指向同一个对象,一致性比较。
>>> L1 =[1,2,3]
>>> L2 = [1,2,3]
>>> L3 = L1
>>> L1 == L2
True
>>> L1 is L2
False
>>> L1 == L3
True
>>> L1 is L3
True
前文提到“python为了优化,缓存了不变对象并对其进行复用(一般是小的整数和小的字符串)”,这里用is检测一下,就知道了。
>>> s = 'spam'
>>> s1 = 'spam'
>>> s is s1,s == s1 # 用is测试结果为true,可以发现s和s1共享对象'spam'
(True, True)
>>> s2 = 'a new file' # 长字符串的测试就是一般性结果了
>>> s3 = 'a new file'
>>> s2 is s3, s2 == s3
(False, True)
相对大小的比较注意:字符串是按照字典顺序比较,列表和元组从左向右对每个部分的内容进行比较;字典无法直接比较,必须通过排序之后的键值列表进行比较。
>>> D = {'a':1, 'b':2}
>>> D1 = {'a':3,'b': 1}
>>> L = sorted(D.values())
>>> L1 = sorted(D1.values())
>>> L < L1
True
>>> D < D1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'dict' and 'dict'
当嵌套对象存在时,python能够自动遍历数据结构,并从左到右递归的应用比较。