
2.1.1 对象的特性
Python使用对象模型存储数据,每一个数据类型都有一个对应的内置类。新建一个数据,就是初始化并生成一个对象,即所有数据都是对象。Python与C/C++不一样,它的数据有自己的特点,一定要记住“一切皆为对象,一切皆为对象的引用”这句话。
Python中的对象有下面3个特性:标识、类型和值。
1.标识(Identity)
Python中的每个对象都有一个唯一标识,对象的标识在对象被创建后不再改变。可以认为对象的标识是对象在内存中的地址,其标识可以由内置函数id()求得。
【例2-1】 下面创建对象2,显示该对象在内存中的地址(标识)。将对象2赋值给变量a后,显示其地址;再将对象2赋值给变量b后,显示其地址;再将变量b赋值给变量c后,显示其地址。


is操作符可以比较两个对象的标识是否相同,即两个对象是否是同一个。

在IDLE中运行上面的代码,以交互方式输入,显示如图2-1所示(在不同的计算机中显示的id()是不同的)。从运行结果看,对象2的地址与对象2的a、b、c标识符的地址相同(8791404015904),也就是说,a、b、c引用了同一个对象,即内存中对象2只占用了一个地址,而不管有多少个引用指向了它,都只有一个地址值,只是会有一个引用计数器记录指向这个地址的引用有几个。
2.类型(Type)
对象的类型决定了对象保存值的类型、可以执行的操作,以及所遵循的规则。可以使用内置函数type()查看一个对象的类型。因为Python中一切皆是对象,type()函数返回的也是对象,而不是简单字符串。
【例2-2】 查看20对象、“hello”对象、True对象的数据类型。

在IDLE中运行上面的代码,以交互方式输入,显示如图2-2所示。

图2-1 创建对象2

图2-2 显示对象的数据类型
3.值(Value)
值是对象表示的数据。Python中的数据分为两种:可变数据类型和不可变数据类型。
1)可变数据类型:值是可变的对象的数据类型称为可变(Mutable)数据类型。
2)不可变数据类型:值一经创建就不可再变的对象的数据类型称为不可变(Immutable)数据类型。
一个对象的可变性由其数据类型决定。数值(Number)、字符串(String)和元组(Tuple)是不可变数据类型。字典(Dictionary)、列表(List)和集合(Set)是可变数据类型。
【例2-3】 不可变数据类型对象的创建。

如图2-3所示,当a=3赋值后,看到a的地址值变了,虽然a的引用不变。a=3、b=a,使得a和b都引用了同一个对象,即3,所以地址值都一样。最后对a进行了加2的操作,所以创建了新的对象5,a引用了这个新的对象,而不再引用对象3。
不可变数据类型可以这样理解,a引用的地址处的值是不能被改变的,也就是8791404015936地址处的值在没被垃圾回收器回收之前一直都是3,不能改变。如果要把a赋值为5,那么只能将a引用的地址从8791404015936变为8791404016000,相当于a=5这个赋值又创建了一个对象,即对象5。而b、c没有被重新赋值,其引用仍然是原来的对象,所以int数据类型是不可变的。如果对int类型的变量再次赋值,在内存中又创建了一个新的对象,则不再是之前的对象。
从上面的过程看出,不可变数据类型的优点就是内存中不管有多少个引用,相同的对象只占用一块内存。它的缺点就是当对变量进行运算从而改变变量引用的对象的值时,由于是不可变数据类型,因此每次改变都将创建新的对象,不再使用的对象被垃圾回收器回收。
【例2-4】 可变数据类型对象的创建。

从运行过程看出,两次a=[1,2,3]操作,a两次引用的地址值是不同的,其实是创建了两个不同的对象,这一点明显不同于不可变数据类型。所以对于可变数据类型来说,具有同样值的对象是不同的对象,即在内存中保存了多个同样值的对象,地址值不同,如图2-4所示。

图2-3 不可变对象

图2-4 可变对象
对列表执行添加元素a.append(4)、a+=[5]操作,发现这两个操作使得a引用的对象值改变了,但是a引用的地址依旧是48842376。也就是说,对a进行添加元素的操作不会改变a引用的地址值,只是改变了地址处存放的值。所以可变数据类型对一个变量进行操作时,其值是可变的,值的变化并不会引起新建对象,即地址是不会变的,只是地址中的值变化了。
需要注意,对可变数据类型的操作不能是赋值操作,比如a=[1,2,3,4,5,6,7]、a=a+[6],这样的操作就不是改变值了,而是新建了一个新的对象,这里的可变只是对于append、+=等添加元素的操作。
概括上述过程就是,Python中的不可变数据类型不允许变量的值发生变化,如果改变了变量的值,就相当于新建了一个对象。相同值的对象在内存中实际只有一个对象,内部会有一个引用计数来记录有多少个变量引用这个对象。
可变数据类型允许变量的值发生变化,即如果对变量进行append、+=等添加元素操作,则只改变变量的值,而不会新建一个对象,变量引用对象的地址也不会变化。相同值的不同对象在内存中存在不同的对象,即每个对象都有自己的地址,相当于在内存中对于同值的对象保存了多份,这里不存在引用计数,是实实在在的对象。