*注:该系列文章主要是对Python深入的学习的记录。
一切皆对象
Python 是一门面向对象语言。在Python的世界里,一切皆对象。
在面向对象的编程语言中,基本都存在 类型(如int型)以及对象。在Python中,类型也是一种对象(整数型是一个对象,字符串类型也是一个对象,class关键字定义的类也是一个对象)。Python的类型也可称为类型对象。
>>> int
<class 'int'>
这里的整数类型int便是一种类型对象。
而常规意义上的对象便是通过类型对象进行实例化操作所得,也叫着实例对象。
>>> int('100')
100
这里int()的过程便是实例化的过程,而得到100则属于实例对象。在Python中,一个整数是一个对象,一个字符串也是一个对象。
Python不区别对待类型(包括基本数据类型或者是class定义的其他类)和对象,所有的类型在python内部均由对象实现,这便是Python一切皆对象的由来。
类型、对象体系
Python3提供了内置函数type(),可以返回对象的类型,而针对类型,则提供了__mro__属性,可以查看类型的继承关系。
>>> type(100)
<class 'int'> # 实例对象100的类型是int
>>> type(int)
<class 'type'> # 类型对象int的类型是type
>>> type(type)
<class 'type'> # 类型对象type的类型是type自身
>>> int.__mro__
(<class 'int'>, <class 'object'>) # 类型对象int的基类是object
>>> type.__mro__
(<class 'type'>, <class 'object'>) # 类型对象type的基类是object
>>> type(object)
<class 'type'> # 类型对象object的类型是type
可以根据已掌握的知识点,比如object是所有类的基类,type是所有类型的元类等,结合上面的结果,归纳出类型和对象的体系。
可以看到,所有类型的基类都是object,所有类型的类型(元类型)都是type,包括type自己。
对象存储
知道了类型与对象的体系,了解一下对象的存储。以整数1为例子。
>>> a = 1
>>> id(a)
9083104
>>> b = a
>>> id(b)
9083104
>>> id(1)
9083104
根据上面的结果,可以看出,变量a、b、 整数1这三者的“地址”都是一样的。
根据其他的编程语言,这是比较奇怪的现象,比如在C中,变量a和变量b的内存空间应该是独立的,并且会将值进行拷贝,如下面的图例。
但在python中,一切皆对象,变量只是一个与对象关联的名字而已,这些变量名将在程序运行时存储于名字空间内,保存了指向实际对象的指针,进而与对象进行绑定。
变量的拷贝只是变量名所保存的指针的拷贝,并不拷贝指针背后的对象。当然,python也提供了拷贝对象的方式,比如copy.copy或者copy.deepcopy。
那么如果将变量的值进行修改,变量名所保存的指针是否会更改?
>>> a = 1
>>> id(a)
9083104
>>> a += 1
>>> id(a)
9083136
可以得出结论,如果整数变量的值被修改,该变量的地址也将被改变。
这里可能跟其他的编程语言差别也比较大,通常值被修改,只需要更改变量所指向的内存地址内的值,而不会更改变量所保存的指针。这里便涉及到python的对象设计---- 可变对象和不可变对象。
在Python中,部分基本类型属于不可变类型,比如整数类型,其整数对象是不可变对象。修改整数对象时,python将以新数值创建一个新对象(这里存在优化手段,暂不涉及),变量名与新对象进行绑定;旧对象如无其他引用,将被释放。
而可变对象则是指创建后可以修改的对象,在python中,典型的可变对象便是 列表(list):
>>> a = [1, 2]
>>> id(a)
140441701802056
>>> a.append(3)
>>> id(a)
140441701802056
根据结果可以看出,列表的值发生了改变,但变量存储的指针并没有改变。这与可变对象的实现有关,在python中,列表对象内部维护了一个动态数组,用以存储元素对象的指针。
如此,当修改列表时,只需要修改动态数组即可,比如添加一个元素3。