Python特性
目录 |
1 动态类型
Python并不声明对象的类型,当在运行时,类型才被解释器判断,变量会自动成为其被指定的类型而使用,这也是其灵活性与方便的一种体现。
Python在声明一个变量时,例如 a = 3, 会执行以下三步:
- 生成一个对象来表示数值3;
- 当变量a不存在时,生成变量a;
- 把变量a与第一步的对象连接起来,也可以说,变量a现在是上述对象的一个引用。
因此,可以说,类型是随着对象的存在而存在的,而不是变量,变量只是在合适的时间表现出不同的类型而已,如下例,变量随着被赋值的不同而表现不同的类型:
a = 3 # It's an integer a = 'spam' # Now it's a string a = 1.23 # Now it's a floating point
2 内存回收
Python的数据类型是动态的,不像其他静态语言一样,如果是C++或C,就会出现内存泄漏的情况,那Python是如何回收内存呢?
Python的类型只与对象相关联,变量只是一个引用,而这个引用就存在与对象之中,对象里面有一个计数器,表示了指向这个对象的引用,如果一个对象的所有引用都指向别的对象或者被删除,那么这个对象的计数器就会被置0,系统就会回收这部分内存了。
3 共享引用
因为Python是类型独立的,与变量无关,因此就会出现以下这种情况:
a = 3 b = a # 3 a = 1.23 # Now it's a floating point b # 仍然是3 b = a # 1.23 a = a + 2 # a is 3.23, a指向了一个新的对象 b # 仍然是1.23
变量始终指向对象,而不是它被赋值时的引用。另外需要注意一点的是,上面代码里的数值3, 1.23, 3.23都是不可改变的(immutable),也就是说,不能通过变量对这个对象进行覆盖,a = a + 2只是使a指向了一个新的对象。这也引出的一个值的可变性的话题,请查看#数值的可变性(mutable or immutable)
但对于可变类型的数据,比如list, dict, set等,却是可以通过该类型数据允许的方法修改的,也就是说,修改这个对象并不是在内存里新开辟一个区域,而是在原区域更改的,这就是可修改的数据。 当然,如果将L1直接赋值另一类型的对象,那么,L2指向的仍然是原list对象,如:
L1 = [2, 3 ,4] L2 = L1 L2 # [2, 3, 4] L1 = 1 L2 # [2, 3, 4]
使用list的方法修改list,就会产生覆盖了:
L1 = [2, 3, 4] # A mutable object L2 = L1 # Make a reference to the same object L1[0] = 24 # An in-place change L1 # [24, 3, 4], L1 is different L2 # [24, 3, 4], But so is L2!
上面的代码,L1修改了list的值,L2的值也跟着改变了,因为list是可修改的,它是原来的对象,并不是新的对象。
如果要避免L2被修改,可以通过拷贝对象的方法,有两种方法:
切片,切片只能应用于list,对dict和set是无效的:
L1 = [2, 3, 4] L2 = L1[:] # Make a copy of L1 L1[0] = 24 L1 # [24, 3, 4] L2 # [2, 3, 4], L2 is not changed
拷贝,可应用于任何可修改对象:
import copy X = copy.copy(Y) # Make top-level "shallow" copy of any object Y X = copy.deepcopy(Y) # Make deep copy of any object Y: copy all nested parts
4 共享引用与相等性测试
Python有两个相等的测试符,一个是==,一个是is,前者是值的测试,也就是值相等就会返回true,后者是对象的测试,只有两者指向相同的对象才返回true。
L = [1, 2, 3] M = [1, 2, 3] # M and L reference different objects L == M # True, Same values L is M # False, Different objects
但例外的情况时有发生:
X = 42 Y = 42 # Should be two different objects X == Y # True X is Y # True, Same object anyhow: caching at work!
为什么对第一段代码好用的情况,在第二段代码却不好用了呢?呵呵,源于Python的cache技术,Python会对一些小的整数或者字符串放入缓存中,以备之后使用,这样可以提高效率,因此,有时即使一个对象的所有引用都消失了,这个对象仍有可能存在于内存之中,回到上例,此处Python的缓存就起作用了,它们直接指向了相同的对象。
另外我们可以使用sys.getrefcount方法查看一个对象有多少引用,但此处并不是仅在你所运行的脚本或对象里,而是你所运行的程序的整个进程里:
import sys sys.getrefcount('klniu') # 3, 3 pointers to this shared piece of memory
5 数值的可变性(mutable or immutable)
6 参考文献
- Mark Lutz. Learning Python, Fourth Edition[M]. United States of America:Newgen North America,2009.09.[2011-08-10]. ISBN:978-0-596-15806-4.