本篇简单讨论python的动态类型,它对于入门python开发来讲是必不可少的。首先来看几行只有动态类型语言才可以这么写的代码

>>> a = 3
>>> a = 'hello,python'
>>> a = [1,2,3,4,5]

我先后给a赋值了数字类型、字符串类型和列表类型,那么问题来了,a到底是什么类型?

有人可能会觉得,a最后被赋值为列表类型,那它最终应该是列表类型吧。正确的答案是:a是没有类型的。

变量、对象和引用

a = 3为例,python创建了一个值为3的整数类型的对象,然后通过a来指向它。a是变量(variable),3是对象(object),变量a是对象3的一个引用(reference),简单的说就是:变量是一个指针,指向一个对象。

类型属于对象,而不是变量

在python中,变量是没有类型的,而对象是有类型的,类型只存在于对象上。python的变量就是在特定的时间引用了一个特定类型的对象。

python对象有两个头部信息,一个是类型标识符(一个指向特定类型对象的指针)和一个引用计数器。前者标识了对象的类型,后者决定何时对象被回收。

对象的垃圾收集

>>> a = 3
>>> a = 'hello,python'
>>> a = [1,2,3,4,5]

上面的代码连着创建了三个对象,而对象a指向最后一个列表对象,前两个对象由于没有任何变量指向它,所以会被自动回收掉。这个过程是自动的,无需程序员编写任何代码。

一提到引用计数器,肯定有人想到了循环引用之类的特殊情况,相信python肯定也考虑到了这些问题,并且处理的很好。

共享引用的问题

>>> a=2
>>> b=a
>>> a+=2
>>> a
4
>>> b
2

在上面的代码中,a和b同时指向对象2,然后通过a += 2来修改对象a,但最终的结果是a=4,b=2。我最初以为的最终结果是a和b都等于4。由于数字类型是不可变的,所以,a += 2会使a指向一个新的对象,这个新对象的值是4,而b还指向原来的对象2。

再看下面的代码

>>> a = [1,2,3,4,5]
>>> b = a
>>> a.append(6)
>>> b
[1, 2, 3, 4, 5, 6]

a和b同时指向一个列表,通过a.append(6)来修改列表,b也会跟着改变。这是我们期待的结果。由于列表类型是可变的,所以append()无需创建一个新的列表。

在python的核心数据类型中,数字、字符串、元组是不可变的,而列表、字典、集合(set)是可变的。

对象的相等问题

在python中有两个操作符经常用来比较两个对象是否相等:

  • ==操作符,比较两个对象的值是否相等
  • is操作符,比较两个对象是否是同一个对象,或者说比较指针是否相等
>>> a = [1,2,3,4,5]
>>> b = a
>>> a == b
True
>>> a is b
True

上面的代码中,变量a和b指向同一个对象,所以,’==操作符’和’is操作符’比较的结果都是相等的。

>>> a = [1,2,3,4,5]
>>> b = a[:]
>>> a == b
True
>>> a is b
False

上面的代码中,a和b各自指向一个值相等的列表,所以,’==操作符’比较的结果相等,而’is操作符’比较的结果是不相等。

>>> a = 3
>>> b = 3
>>> a == b
True
>>> a is b
True

上面又是怎么回事呢?这是由于python在创建3这个对象时偷了个懒,python的缓存机制(较小的整数和字符串会被缓存并复用)在此生效了。下面的代码证明了python只会缓存较小的数字

>>> a = 1212313
>>> b = 1212313
>>> a == b
True
>>> a is b
False

与其它语言的比较

  • 与传统静态类型语言比较:传统的静态类型语言(C和类C语言)的变量是有类型的,而python的变量是没有类型的
  • 与java和c#等托管语言比较:python没有值类型或引用类型的概念,只有可变类型和不可变类型
  • 与javascript比较:一个javascript对象可以在运行时被赋予新的属性和方法,好像更加动态,完全没有类型的概念。而python是有类型的,特定的类型对应着一系列特定的操作。