为什么80%的码农都做不了架构师?>>>
对于Python来说,当写下:
a = 3
时候,Python如何知道a是代表一个整型呢?实际上Python并不知道,但是Python是运行时候自动进行匹配的,所以运行时候就知道a是整型.这跟C++的RTTI差不多,类似模板的技术.
变量,对象和引用
变量通常关联三个步骤:1.变量创建,当你第一次对变量进行赋值时候,变量被创建(非赋值而使用变量,则会产生异常). 2.变量类型,变量通常是没有类型的,它关联到整型,则它就是整型,它关联到字符串型,则它就是字符串型. 3.变量使用,只要变量被赋值,那么它就可以被使用.
但是这里有一个至关重要的概念:引用
>>> a = 3
>>> id(a)
33120312L
>>> a = 4
>>> id(a)
33120288L
这里a = 3,实际上是将变量a指向了
对象3,而如果我们重新给a赋值4,则会发现其id已经改变(因为3和4是不同的两个对象).可形象化的表示如下:
可用以下三个步骤解释上图:
1. 变量进入系统表,用于存储一个链接指向对象.
2. 对象存在于所分配的内存中.
3. 引用自动关联其变量和对象.
类型存活于对象之中,而非变量
我们可以使用如下的代码轻而易举的说明:为什么类型存活于对象之中,而非变量:
>>> a = 3
>>> id(a)
33120312L
>>> a = 'spam'
>>> id(a)
39014152L
>>> a = 1.23
>>> id(a)
35872888L
对象是垃圾收集的
Python是使用引用计数方式来进行垃圾收集.例如上例中当a重新被赋值'spam'时候,由于对象3的引用计数从1到0(给a赋值3时候3的引用计数加1),所以自动进行了垃圾回收.
但是有个问题是:如果Python中发生,两个对象互相引用,那么引用计数的方式是否会失效?还是Python本身也可以使用"离开作用域则对象失效"的垃圾收集方式.
这确实无法避免(互相引用),所以有些Python实现使用了"离开作用域则进行变量销毁"的垃圾收集机制.
共享引用
当我们执行以下代码的时候,就产生了对象的共享:
>>> a = 3
>>> b = a
>>> id(a), id(b)
(33120312L, 33120312L)
用流程图表示如下:
但是,当我们执行以下代码的时候,对象改变了:
>>> a = 3
>>> b = a
>>> a = 'spam'
>>> id(a), id(b)
(41457024L, 33120312L)
流程图如下:
这里我们是将a指向了新的对象.但是如果a和b本身所指向的对象是可改变的,则会产生Python中著名的"浅拷贝和深拷贝"问题:
>>> person = ["name", ["savings", 100.00]]
>>> hubby = person[:]
>>> wifey = list(person)
>>> [id(x) for x in (person,hubby,wifey)]
[40895560L, 40894920L, 40889032L]
>>> hubby[0] = "joe"
>>> wifey[0] = "jane"
>>> hubby, wifey
(['joe', ['savings', 100.0]], ['jane', ['savings', 100.0]])
>>> hubby[1][1] = 50.00
>>> hubby, wifey
(['joe', ['savings', 50.0]], ['jane', ['savings', 50.0]])
而一般这时候,我们需要的是深拷贝:
>>> person = ["name", ["savings", 100.00]]
>>> hubby = person
>>> import copy
>>> wifey = copy.deepcopy(person)
>>> [id(x) for x in (person, hubby, wifey)]
[40889608L, 40889608L, 40863624L]
>>> hubby[0] = "joe"
>>> wifey[0] = "jane"
>>> hubby, wifey
(['joe', ['savings', 100.0]], ['jane', ['savings', 100.0]])
>>> hubby[1][1] = 50.00
>>> hubby, wifey
(['joe', ['savings', 50.0]], ['jane', ['savings', 100.0]])
备注:所谓的深拷贝,就是不同对象具有相同的值而已(值的对象也不同)
共享引用和相等
对于垃圾回收来说,如果回收的对象不可改变(如整型,集合,字符串等等,这里以42举例),则Python解释器一般不回收,而让42一直存在于系统表中直到重新被赋值.
Python中有两种判断的方法:==和is.==等于号用于判断两个对象的值是否相等,而is用来判断两个对象是否属于同一个对象类型:
>>> L = [1, 2, 3]
>>> M = L
>>> L == M
True
>>> L is M
True
但是,当L和M指向不同的可改变对象时候,情况就不一样了:
>>> L = [1, 2, 3]
>>> M = [1, 2, 3]
>>> L == M
True
>>> L is M
False
而下列代码恰恰说明了不可改变对象通常并没有被回收:
>>> X = 42
>>> Y = 42
>>> X == Y
True
>>> X is Y
True
而我们可以通过方法getrefcount来查看对象被引用的次数:
>>> import sys
>>> sys.getrefcount(42)
13