学习环境:

操作系统: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函数有如下代码:

[code lang=”C”]
void Print(PyObject* object)
{
object->ob_type->tp_print(object);
}
[/code]

解释一下,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行左右有如下代码:

[code]
#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)
[/code]

略微介绍一下代码:

_Py_NewReference(op):将对象的引用计数初始化为1;
_Py_Dealloc(op):释放对象(析构对象)
Py_INCREF(op):引用计数加一
#define Py_DECREF(op):引用计数减一
最后说一下python中对象的分类,大致分为5类
Fundamental对象:类型对象
Numeric对象:数值对象
Sequence对象:容纳其他对象的序列集合对象
Mapping对象:关联对象
Internal对象:python虚拟机在运行时内部使用的对象


好了,今天先写到这了。