欢迎关注微信公众号——Python与统计分析,一起学习,一起交流。
相信所有学过Python的人都听过这样一句话:Python中,一切皆对象。一个整数是一个对象,产生这个整数的类int
也是一个对象。函数是一个对象,函数所在的模块也是一个对象。最让我们惊讶的是,就连代码本身也是对象。
Python把面向对象的思想发挥的淋漓尽致,然而对于初学者来说,我们还是不清楚什么是对象。Python官方解释器是由C语言编写的,解答这个问题需要从C语言中寻找答案。因此本文,我们主要从C的层面来认识对象。
对象机制的核心——PyObject
对于人来说,对象是一个比较具体的概念,比如说一个人,一张书桌。而对于计算机而言,对象却是一个抽象的概念。通常的说法是,对象是数据以及操作这些数据的方法的集合。由于数据和操作数据的方法都存储在计算机内存中,你可以理解一个对象就是计算机中存储这些数据和方法的一块内存空间,而这块内存可能是连续的,也可能是不连续的。如果是不连续的,我们需要从整体上来把握。再具体一些,从C的层面来看,Python的对象就是为C中的结构体在内存堆空间中申请的一块内存。
Python中有很多种类型的对象,不同类型的对象之间存在一些共性,这些共性定义在PyObject结构体中。
从Python官网下载Python解释器的源码包,解压打开Includeobject.h,我们就可以看到PyObject了,PyObject的定义如下:
//Include/object.h
typedef struct _object {
_PyObject_HEAD_EXTRA
Py_ssize_t ob_refcnt;
struct _typeobject *ob_type;
} PyObject;
可以看到PyObject
内部定义了一个_PyObject_HEAD_EXTRA
、一个ob_refcnt
和一个ob_type
。_PyObject_HEAD_EXTRA
是一个宏,这个宏主要使用来维护一个“双向链表”的,通常不启用,可以不用管。我们的关注点放在ob_refcnt
和ob_type
。
ob_refcnt
:即reference count,即引用计数。ob_type
:类型指针。
引用计数表示该对象被变量引用的次数,对象被引用1次,ob_refcnt就会加1,引用解除时,ob_refcnt就会减少1。引用计数是Python实现对象回收的重要机制之一。
我们注意到ob_type指向了一个_typeobject
的结构体,这个结构体是做什么的呢?我们说对象有三个重要特征:id、类型和值。_typeobject
就是用来指定一个对象的类型的。这部分内容我们后面再进行分析。
现在我们看到了,Python中对象至少包含两部分内容,一个是引用计数,一个是类型,这些是对象的共性,然而还有一些性质虽然不是所有对象所共有,但是是大部分对象所共有的,接下来我们就会看到。
定长对象与变长对象
Python中根据对象所占用的内存空间大小是否固定,可以将对象分为定长对象和变长对象。定长对象如整数。变长对象如字符串、列表等。字符串、列表中有多少个元素,都无法事先确定,只能使用变长对象来进行存储,变长对象在C层面对应于PyVarObject结构体。
同样可以在object.h中找到PyVarObject结构体:
//Include/object.h
typedef struct {
PyObject ob_base;
Py_ssize_t ob_size; /* Number of items in variable part */
} PyVarObject;
可以看到,PyVarObject是对PyObject的扩展,在其基础上增加一个ob_size的字段。ob_size指明了变长对象中容纳了多少个元素。比如对于一个list,它就是一个变长对象,list中有5个元素,ob_size就等于5。
Python中的对象都是在PyVarObject或者PyObject基础上进行的扩展,例如:float实例对象,其结构体定义为:
typedef struct {
PyObject_HEAD
double ob_fval;
} PyFloatObject;
可见,float实例对象在内存中对应PyFloatObject结构体,其是在PyObject基础上增加了一个双精度的ob_fval,其代表的就是该实例对象的值。
再如list实例对象,其结构体的定义为:
typedef struct {
PyObject_VAR_HEAD
PyObject **ob_item;
Py_ssize_t allocated;
} PyListObject;
list实例对象是一个PyListObject的结构体,是在PyVarObject的基础之上增加了两个字段:
- ob_item: 指向 动态数组 的指针,数组保存元素对象指针;
- allocated: 动态数组总长度,即列表当前的 容量
float实例对象和list实例对象在介绍相应数据类型的时候再进行阐述。
类对象
除了实例化对象之外,Python中类也是一种对象,我们称之为类对象。类对象之间也有一些共性,接下来我们看就来看一下。
首先我们要明确,类对象作为一种对象,其基本结构应该是在PyObject基础上扩展的,具体而言Python中的类对象是以PyTypeObject结构体为基础的,现在我们请出PyTypeObject的结构体,看看其真身如何。
typedef struct _typeobject {
PyObject_VAR_HEAD
const char *tp_name;
Py_ssize_t tp_basicsize, tp_itemsize;
destructor tp_dealloc;
printfunc tp_print;
getattrfunc tp_getattr;
setattrfunc tp_setattr;
PyAsyncMethods *tp_as_async; /* formerly known as tp_compare (Python 2)
or tp_reserved (Python 3) */
reprfunc tp_repr;
PyNumberMe