`

Python源码剖析(2)——对象的创建

阅读更多

6 对象的创建

学习了上一篇博客的内容,我们可以进一步问一个问题了:假如我们命令python创建一个整数型对象,python内部是怎样从无到有创建一个对象的?

一般来说,方法有两种:一是通过C API创建,一是通过类型对象PyInt_Type创建。

1 C API创建

所谓C API,就是python设计者在C语言源码中预留给开发者的现成的函数接口,封装好了新建、插入、析构等基本操作,方便用户自由拓展python的功能,当然,他们在python内部也是大量调用的。以新建对象为例,C API主要有两种形式:

    1 PyObject* intobj = PyObject_New(Pyobject, &PyInt_Type);
    2 PyObject* intobj = PyInt_FromLong(10);

 

第一种方法来自PyObject,是python内部所有类型通用的,可以返回一个某类型的对象,我们可以在objimpl.h头文件中见到他的真容:

 

【objimpl.h】
#define PyObject_New(type, typeobj) \
             ( (type *) _PyObject_New(typeobj) )

 

 

这是一个宏定义,首先抛开_PyObject_New内部的细节,我们知道上面的第一行代码会返回一个PyObject类型的对象。然后再来看_PyObject_New函数:

 

【Object.c】
_PyObject_New(PyTypeObject *tp)
{
    PyObject *op;
    op = (PyObject *) PyObject_MALLOC(_PyObject_SIZE(tp));
    if (op == NULL)
        return PyErr_NoMemory();
    return PyObject_INIT(op, tp);
}

 

 

这里为PyObject类型的对象op开辟了空间,也就是新建了一个PyObject对象,然后转到PyObject_INIT。这次,我们干脆刨根问底:

    【objimpl.h】
#define PyObject_INIT(op, typeobj) \
    ( Py_TYPE(op) = (typeobj), _Py_NewReference((PyObject *)(op)), (op) )//很喜欢这里逗号表达式的巧妙运用

    【object.h】
#define Py_TYPE(ob)             (((PyObject*)(ob))->ob_type)

    【object.c】
void
_Py_NewReference(PyObject *op)
{
    _Py_INC_REFTOTAL;
    op->ob_refcnt = 1;
    _Py_AddToAllObjects(op, 1);
    _Py_INC_TPALLOCS(op);
}

 

我们一口气找齐了创建对象过程中几乎所有的操作,现在梳理一下:

    1 新建一个PyObject类型的指针并为它开辟内存空间

    2 设置他的ob__type,是它称为指定类型

    3 设置新对象的引用次数

所以,我们对第一种C API创建对象的过程已经很熟悉了。顺便提一下,相似的API还有很多,他们的格式均为PyObject_***,都是基于PyObject,但系统会根据对象类型的不同最终执行不同的函数。

与第一种API的普适性相对,第二种API是python为每种对象量身定制的,比如PyIntObject有PyInt_FromLong、PyInt_FromString等,PyStringObject有PyString_FromFormat、PyString_FromStringAndSize等等,具体的功能实现与函数名对应,再此只举一例:

【intobject.c】
PyObject *
PyInt_FromLong(long ival)
{
…
/* Inline PyObject_New */
    v = free_list;
    free_list = (PyIntObject *)Py_TYPE(v);
    PyObject_INIT(v, &PyInt_Type);
    v->ob_ival = ival;
    return (PyObject *) v;
}

 

这里的free_list涉及到python的缓冲机制,暂时就到后面讲,但是其他的操作,我们也都很熟悉,过程如下:

1 得到内存空间(这里没有用molloc函数分配,而是直接从缓冲池获取,这个后面再详细讲)

2 设置类型

3 初始化引用次数

4 设置自己的参数值(这里是整数的数值ob_ival

   

2 通过类型对象创建

分析了两种通过python内部C API创建对象的方式,我们已经对python内部对象的创建过程有了比较清楚的认识,但是有一个问题你可能也注意到了,那就是这些强大的API永远只是对python的内建对象有效,而对于我们用户自定义的类型,比如calss A这样的对象,pythonAPI就是束手无策了,因为系统不可能事先准备好PyA_New这样的函数。

但是,对于A这样的类型,python又是怎样创建实例的呢?这就需要我们把注意力从最底层的代码收回,看看python在调用自己的C API之前做了那些操作。

首先,回顾一下上篇博客中我们的结论,那就是最基本PyObject结构体中只有两项内容:

int ob_refcnt;               \
    struct _typeobject *ob_type;

 

_refcnt是个整数,新建对相关的操作显然跟他关系不大,据此,我们有理由相信,python中创建对象的秘密就隐藏在ob_type中了。让我们回到typeobject的定义:

【object.h】
    /* Attribute descriptor and subclassing stuff */
    struct PyMethodDef *tp_methods;
    struct PyMemberDef *tp_members;
    struct PyGetSetDef *tp_getset;
    struct _typeobject *tp_base;
    。。。
    initproc tp_init;
    allocfunc tp_alloc;
    newfunc tp_new;
    freefunc tp_free; /* Low-level free-memory routine */
    inquiry tp_is_gc; /* For PyObject_IS_GC */
    。。。
    destructor tp_del;

 

从这段代码是从object.h中截取的PyTypeObject中的部分属性。其中,tp_base是当前类型对象的基类,跟java父类的概念差不多;tp_new即为对象的创建方法。我们在创建对象时,一般会指定对象的类型,因此python就会从指定的类型对象中查找tp_new方法,执行新建的操作。为了说明这一点,我们做个简单的实验:

首先,创建一个整数对象i:

i = 10

 

python中,我们是可以直接看到对象的tp_basetp_new的:

    >>> i = 10
    >>> type(i)
    <type 'int'>
    >>> int.__new__
    <built-in method __new__ of type object at 0x1E221000>
    >>> i.__new__
    <built-in method __new__ of type object at 0x1E221000>
    >>> int.__base__
    <type 'object'>
    >>> object.__new__
    <built-in method __new__ of type object at 0x1E2272A8>

 

我们发现,整数i中有他的类型对象int相同的__new__函数,而且int的__new__函数与它的基类object的__new__函数不同。另外,我们也可以相信,int与object的__new__函数一定为我上面提到的C API,是python内部已经写好的。现在,我们可以回到之前的问题了:对于自定义的类型,我们没有实现为它编写任何__new__函数,它的__new__函数又如何呢?

为此,我们测试一个简单的不能再简单的自定义类型A:

class A(int):
    pass
>>> A.__base__
<type 'int'>
>>> A.__new__
<built-in method __new__ of type object at 0x1E221000>
>>> A.__base__.__new__
<built-in method __new__ of type object at 0x1E221000>
>>> A.__base__.__base__.__new__
<built-in method __new__ of type object at 0x1E2272A8>

 

因此,我们很清楚地看到,A的__new__正是来自它的基类int

如果你还不太确定,我们不妨再测试一个自定义类B:

    class B(A):
       pass
    >>> B.__base__
    <class '__main__.A'>
    >>> B.__new__
    <built-in method __new__ of type object at 0x1E2272A8>
    >>> B.__base__.__new__
    <built-in method __new__ of type object at 0x1E2272A8>

 

现在,我们应该有理由相信了,python中创建对相关的机制是这样的:

1 从类型对象A中寻找tp_new方法

2 如果没找到,转而找到它的基类object,在object中找tp_new方法

3 以此类推,直到找到某个有tp_new的基类,返回他的方法

我们也知道,即使是自定义类型,python的也规定它必须继成某一基类,而通过层层追溯,最后总会找到一个是系统内建对象的基类,这时,就可以调用对应类型的tp_new方法,也就是我们上面介绍到的C API。当然,通过修改类中的__init__方法,我们还可以针对自己的类补充创建时的初始化操作。

 

对于python中对象的创建过程,今天就简单剖析到这了。事实上,python具体创建对象时,还会有许多复杂的操作,如缓冲池的使用等,这些后面会详细分析。持续关注啊!

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics