python源码学习(二)——对象

By | 2014/06/13

学习环境:
操作系统:Ubuntu 12.04 STL
Python版本:2.7
在Python中,所有的东西都是对象,什么类型啦,结构啦都是对象,所有的对象都有相同的内容,这些内容在Python源码的PyObject中定义,现在我们打开Python源码,include文件夹下的object.h文件,找到大约在第106行左右,有PyObject的结构体,代码如下:

typedef struct _object {
    PyObject_HEAD
} PyObject;

从代码里可以发现Python对象的所有内容都在PyObject_HEAD这个宏中,那么我们再来看看这个宏是怎么定义的,低吗如下:(大约在第78行左右)

#define PyObject_HEAD                   \
    _PyObject_HEAD_EXTRA                \
    Py_ssize_t ob_refcnt;               \
    struct _typeobject *ob_type;

阅读以上代码,我们会发现*ob_type是一个指向_typeobject结构体的指针,他其实对应着Python中一个非常特殊的对象——指定一个对象类型的类型对象;另外,ob_refcnt是Py_ssize_t结构体的一个实例,与python的内存管理有关,用来实现基于自动引用计数器的垃圾回收机制,在python2.5版本中,这个ob_refcnt是很简单的,int类型,每当有一个PyObject*引用X对象时,X对象的ob_refcnt加一,当PyObject*被删除时,X对象的ob_refcnt减一当减到0时,将X对象从堆中释放,将空间供别的对象使用。在python2.7中,这个稍微做了一些修改,ob_refcnt成了Py_ssize_t的实例,但本质没有发生变化,依然是通过引用计数器和环路检测来实现垃圾回收机制,如果你把Py_ssize_t ob_refcnt删掉,在编译安装python,会出现一大堆错误,原因就是对象的垃圾处理机制根本就存在巨大的错误。
那么现在又有一个新的问题了,如果python所有的对象都只包含PyObject,那岂不是python只有一种对象了吗?如果真这么认为,那就滑天下之大稽了,举个例子,我们还在include文件夹下,打开intobject.h文件,我们找到如下代码:(大约在第23行左右)

typedef struct {
    PyObject_HEAD
    long ob_ival;
} PyIntObject;

不难看出,他除了PyObject_HEAD这个宏,还有一个long类型的变量ob_ival,这个变量就是来存储整数的值的,举个例子,我们在python中写i=1,实际上1不是数字,而是一个对象,这个对象的属性值(也就是源代码中PyIntObject实例的ob_ival属性的值)是1,这样,我们就看出python中不同类型对象之间的不同点了吧。有些人在打开listobject.h的时候,看到如下代码可能会问这个结构体为什么没有PyObject_HEAD?没关系,仔细看看如下代码:

typedef struct {
    PyObject_VAR_HEAD
    /* Vector of pointers to list elements.  list[0] is ob_item[0], etc. */
    PyObject **ob_item;

    /* ob_item contains space for 'allocated' elements.  The number
     * currently in use is ob_size.
     * Invariants:
     *     0 <= ob_size <= allocated
     *     len(list) == ob_size
     *     ob_item == NULL implies ob_size == allocated == 0
     * list.sort() temporarily sets allocated to -1 to detect mutations.
     *
     * Items must normally not be NULL, except during construction when
     * the list is not yet visible outside the function that builds it.
     */
    Py_ssize_t allocated;
} PyListObject;

代码中有PyObject_VAR_HEAD一个宏,我们依然打开object.h这个文件,在第96行左右,有如下宏定义:

#define PyObject_VAR_HEAD               \
    PyObject_HEAD                       \
    Py_ssize_t ob_size;

我们会发现PyObject_HEAD,因此,在结构体中出现PyObject_VAR_HEAD也就拥有了PyObject_HEAD,如下图所示:
Screen Shot 2014-06-13 at 上午11.24.47
从上图可以看出所有的对象都继承于PyObject,只是对象所特有的属性有所不同。
接下来我们讲一下定长对象和变长对象,所谓定长对象就比如说int啦,等等,而变长对象就比如说list,dict,string等等,定长对象如int,不管你给个多大的数,10000也好,1也好,占用的内存都是一样大,而变长对象,比如说string对象,”HelloWorld”和”Hello World”所占内存就不一样大,我们打开include文件夹下的object.h文件,找到96行和第110行,会发现如下代码:

#define PyObject_VAR_HEAD               \
    PyObject_HEAD                       \
    Py_ssize_t ob_size; /* Number of items in variable part */

typedef struct {
    PyObject_VAR_HEAD
} PyVarObject;

在上面我们看过了,string和list中都继承了PyVarObject,并且都有PyObject_VAR_HEAD这个宏,并且从源码中我们发现list和string都有都有一个sizeof函数,在string中,sizeof指的是字符串所容纳的字符的个数,而list中的sizeof值得则是list中元素的个数。PyObject_VAR_HEAD只是对PyObject_HEAD的一个扩充,对于任何一个PyVarObject所占的内存其开头部分所占的意义和PyObject一样,因此,我们只要一个指向PyObject对象的指针就可以引用任意的一个对象。int,string,list,dict对象与PyObject和PyVarObject的关系如下图所示:
Screen Shot 2014-06-13 at 下午4.59.37
好了,今天向写到这,下一次写一写有关类型对象和对象创建等。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

This site uses Akismet to reduce spam. Learn how your comment data is processed.