美文网首页Python中文社区IT狗工作室
第1篇:CPython实现原理:万物皆为PyObject

第1篇:CPython实现原理:万物皆为PyObject

作者: 铁甲万能狗 | 来源:发表于2020-06-27 10:29 被阅读0次

    对象的定义

    在C/C++中,对象就是堆(Heap)内存中的内存实体,从简单的基本数据类型(int,float,char)到聚合的数据类型(struct)一切皆为对象,我们说基本的数据类型是简单的对象(Simple Object),因为它仅包含数据属性。而struct级别的数据类型是完整的对象(Concrete Object),因为完整的对象具有属性行为两个基本概念。

    • 属性就是结构体的数据字段,可以是基本数据类型,指针类型、甚至是嵌套的struct类型。
    • 行为又称为方法成员函数,就是struct内部定义的一系列函数指针。

    备注:如有疑问:请移至《C++ 面向对象》《C++ 多态》

    Python对象的本源 PyObject

    CPython是用C语言实现的,那么用C/C++中关于对象的概念,去理解Python对象也是理所当然的。先看一下CPython中关于PyObject的定义

    typedef struct _object {
        _PyObject_HEAD_EXTRA  
        Py_ssize_t ob_refcnt;     //引用计数器,和内存回收有关
        PyTypeObject *ob_type;  //定义Python对象的元类信息
    } PyObject;
    

    其实整个PyObject的难点是就是第三个字段PyTyepObject,也是整个PyObject的核心,包括基本的类型信息:类名称,类型尺寸(需要分配多大的内存)以及类绑定的方法(即绑定的函数指针)。后文会详细谈到,而_PyObject_HEAD_EXTRA这个宏的定义如下

    #ifdef Py_TRACE_REFS
    /* Define pointers to support a doubly-linked list of all live heap objects. */
    #define _PyObject_HEAD_EXTRA            \
        struct _object *_ob_next;           \
        struct _object *_ob_prev;
    
    #define _PyObject_EXTRA_INIT 0, 0,
    
    #else
    #define _PyObject_HEAD_EXTRA
    #define _PyObject_EXTRA_INIT
    #endif
    

    而以发布模型的编译CPython源代码的话,_PyObject_HEAD_EXTRA这段宏定义是不存在,因此PyObject的定义可以简化为

    typedef struct _object {
        Py_ssize_t ob_refcnt;     //引用计数器,和内存回收有关
        PyTypeObject *ob_type;  //定义Python对象的元类信息
    } PyObject;
    

    在Python的世界观中一切皆为PyObject这个话怎么理解呢?在Python语义中,每当我们实例化任意一个Python对象,在其占用的堆内存区块的首个字节就包含一个PyObject定义的副本,除PyObject的相关内存字节副本外,跟随的内存字节是当前对象的内存信息。举个例子,比如PyLongObject,继承PyObject,我们先看看位于Include/longintrepr.h定义

    struct _longobject {
        PyObject_VAR_HEAD
        digit ob_digit[1];
    };
    

    而PyObject_VAR_HEAD这个宏定义实质上是PyVarObject类型,而PyVarObject本来就继承PyObject,如下Include/object.h

    ....
    #define PyObject_VAR_HEAD      PyVarObject ob_base;
    ....
    typedef struct {
        PyObject ob_base;
        Py_ssize_t ob_size; /* Number of items in variable part */
    } PyVarObject;
    

    我们这里暂不用理会PyVarObject的具体细节,用一个类继承图就比较直观啦


    从内存图的角度来看,如下图所示,PyLongObject类型的对象,我们说当实例化一个PyLongObject的实例,它首先要初始化PyVarObject的内存中的数据(所有字段的默认值),我们说PyLongObject的实例持有PyVarObject的内存副本。而PyVarObject初始化,也要初始化PyObject的内存数据。换句话说,在Python内部,每个堆吸纳个都拥有相同的对象头部,这使对象的引用变得单一化,只需一个PyObject*指针就可以任意引用一个对象

    备注:其实C++面向对象模型大体上是这么一个套路,只不过C++运行时增加了一些对象访问控制设定,而C实现的PyObject是不存在所谓访问控制设定一回事。

    定长对象和变长对象

    在CPython 3.x像整数类型的Python对象,其具体的数字字面量ob_digit[1](注意ob_digit是一个unsigned int数组)。例如

    >>> n=int(1)
    >>> n2=int(9999)
    >>> n
    1
    >>> n2
    9999
    >>> import sys
    >>> sys.getsizeof(n)
    28
    >>> sys.getsizeof(n2)
    28
    

    我们说像int,double这类的基本数据类型,他们不同的实例有固定长度的,我们说这些叫定长对象。而变长对象即对象的类型尺寸是可变的。例如PyStringObject、PyListObject、PyDictObject这些都是可变长对象(也叫容器对象,这个就个C++标准库的容器对象旧非常相似了)。容器对象的最基本的特征。其struct内部维护着一个数据指针(指向堆内存中的数据实体),以及一个计数器就是实时统计有多少个数据实体。目前仅需简单了解这些概念即可。

    PyTypeObject

    我们说过任意一个PyObject的实例创建过程中,需要知道类型名,需要分配的堆内存,以及该对象实例配套的行为(即函数指针),这些信息均包含在PyTypeObject的定义中。如下代码所示,完整代码见Include/cpython/object.h的第193行-274行

    struct _typeobject {
        PyObject_VAR_HEAD
        const char *tp_name; //类型名称
        //内存分配的类型尺寸
        Py_ssize_t tp_basicsize, tp_itemsize; 
        /* Methods to implement standard operations */
    
        destructor tp_dealloc;
        Py_ssize_t tp_vectorcall_offset;
        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;
    
        /* 标准类的匹配的方法 */
    
        PyNumberMethods *tp_as_number;
        PySequenceMethods *tp_as_sequence;
        PyMappingMethods *tp_as_mapping;
    
        /* 更多标准操作(此处为二进制兼容性)*/
    
        hashfunc tp_hash;
        ternaryfunc tp_call;
        reprfunc tp_str;
        getattrofunc tp_getattro;
        setattrofunc tp_setattro;
    
        //函数来访问对象作为I/O缓冲器
        PyBufferProcs *tp_as_buffer;
    
        /* Flags to define presence of optional/expanded features */
        unsigned long tp_flags;
    
        const char *tp_doc; /* Documentation string */
        ...
        //属性描述符和子类相关信息,这里定义
        //这里定义了对象的行为
        struct PyMethodDef *tp_methods;
        struct PyMemberDef *tp_members;
        struct PyGetSetDef *tp_getset;
        struct _typeobject *tp_base;
        PyObject *tp_dict;
        descrgetfunc tp_descr_get;
        descrsetfunc tp_descr_set;
        Py_ssize_t tp_dictoffset;
        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 */
        PyObject *tp_bases;
        PyObject *tp_mro; /* method resolution order */
        PyObject *tp_cache;
        PyObject *tp_subclasses;
        PyObject *tp_weaklist;
        destructor tp_del;
        .....
    };
    

    创建Python对象

    我们这里说的Python对象创建是使用CPython的C函数接口来创建一个PyObject,首先来看一下相关的函数接口

    Py_BuildValue源代码,用法参见官方文档

    //位于Include/modsupport.h源代码文件
    PyAPI_FUNC(PyObject *) Py_BuildValue(const char *, ...);
    
    //位于Python/modsupport.c源代码文件
    PyObject *
    Py_BuildValue(const char *format, ...)
    {
        va_list va;
        PyObject* retval;
        va_start(va, format);
        retval = va_build_value(format, va, 0);
        va_end(va);
        return retval;
    }
    

    PyObject_CallObject源代码,用法参见官方文档

    //函数声明位于Include/abstract.h
    PyAPI_FUNC(PyObject *) PyObject_CallObject(PyObject *callable,
                                               PyObject *args);
    
    //位于Objects/call.c
    PyObject *
    PyObject_CallObject(PyObject *callable, PyObject *args)
    {
        PyThreadState *tstate = _PyThreadState_GET();
        assert(!_PyErr_Occurred(tstate));
        if (args == NULL) {
            return _PyObject_CallNoArgTstate(tstate, callable);
        }
        if (!PyTuple_Check(args)) {
            _PyErr_SetString(tstate, PyExc_TypeError,
                             "argument list must be a tuple");
            return NULL;
        }
        return _PyObject_Call(tstate, callable, args, NULL);
    }
    

    相关文章

      网友评论

        本文标题:第1篇:CPython实现原理:万物皆为PyObject

        本文链接:https://www.haomeiwen.com/subject/rxlmfktx.html