Python源码学习(四)——对象的多态性和引用计数

By | 2014/06/15

学习环境:
操作系统:Ubuntu 12.04 STL
Python版本:2.7
一、多态
Python通过PyObject和PyTypeobject,利用C语言实现了面向对象语言所具备的多态性。在Python创建一个对象,比如说PyIntObject对象时,回分配内存并初始化,然后Python内部会有一个PyObject*变量,而不是通过一个PyIntObject来存储和维护这个对象,其他对象也是如此,所以在Python内部函数之间传递的是一种泛型指针——PyObject*(注:有一些Python的框架,函数之间可能不是传递泛型指针,这里我们只讲Python内建函数,其他框架不做讨论)。这个指针所指向的对象是什么类型的我们不知道,我们只能通过指针所指的这个对象的ob_type域进行动态判断,正式这个域使Python实现了多态。
打开Python源码,print函数有如下代码:

void Print(PyObject* object)
{
    object->ob_type->tp_print(object);
}

解释一下,Print函数根据传入的PyObject*的指针调用ob_type域动态进行判断,而正是通过这个域,调用相应的函数,实现多态机制,python中AOL的C API正是建立在这种多态机制上的。

二、引用计数器
这个在前面的博客中已经说了,Python是通过引用计数器实现的,就是我们看到的Object.h中的ob_refcnt来实现的,在这个实例中,主要通过Py_INCREF(op)和Py_DECREF(op)来实现增加和减少一个对象的引用计数,当引用计数减为0时,Py_DECREF将会掉用tp_dealloc这个函数指针来释放这个实例(Python中的对象),Python通过_Py_NewReference(op)这个宏来实现将对象的引用计数初始化为1。从设计模式来说,Python的引用计数器使用的是observer(观察者)模式来实现的。另外,还有非常重要的两点,一是Python中的对象释放后并不是直接free掉,这个前面的博文中说过,当对象大小小于256bits时,内存将被送回内存池中;二是Python中的类型类型对象(类型的类型对象是一种特殊的类型对象)是永远不会被释放的,从Python一运行他就存在,每个对象中指向类型对象的指针不被视为对该类型对象的引用。
打开Python的源码中include文件夹下的Object.h文件,在大约758行左右有如下代码:

#define _Py_NewReference(op) (                          \
    _Py_INC_TPALLOCS(op) _Py_COUNT_ALLOCS_COMMA         \
    _Py_INC_REFTOTAL  _Py_REF_DEBUG_COMMA               \
    Py_REFCNT(op) = 1)

#define _Py_ForgetReference(op) _Py_INC_TPFREES(op)

#define _Py_Dealloc(op) (                               \
    _Py_INC_TPFREES(op) _Py_COUNT_ALLOCS_COMMA          \
    (*Py_TYPE(op)->tp_dealloc)((PyObject *)(op)))
#endif /* !Py_TRACE_REFS */

#define Py_INCREF(op) (                         \
    _Py_INC_REFTOTAL  _Py_REF_DEBUG_COMMA       \
    ((PyObject*)(op))->ob_refcnt++)

#define Py_DECREF(op)                                   \
    do {                                                \
        if (_Py_DEC_REFTOTAL  _Py_REF_DEBUG_COMMA       \
        --((PyObject*)(op))->ob_refcnt != 0)            \
            _Py_CHECK_REFCNT(op)                        \
        else                                            \
        _Py_Dealloc((PyObject *)(op));                  \
    } while (0)

略微介绍一下代码:
_Py_NewReference(op):将对象的引用计数初始化为1;
_Py_Dealloc(op):释放对象(析构对象)
Py_INCREF(op):引用计数加一
#define Py_DECREF(op):引用计数减一

最后说一下python中对象的分类,大致分为5类
Fundamental对象:类型对象
Numeric对象:数值对象
Sequence对象:容纳其他对象的序列集合对象
Mapping对象:关联对象
Internal对象:python虚拟机在运行时内部使用的对象
Screen Shot 2014-06-15 at 下午3.09.06
好了,今天先写到这了。