• 企业400电话
  • 微网小程序
  • AI电话机器人
  • 电商代运营
  • 全 部 栏 目

    企业400电话 网络优化推广 AI电话机器人 呼叫中心 网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    Python源码学习之PyObject和PyTypeObject

    前言

    Python是C语言实现的,因此Python对象在C语言层面应该是一个结构体 ,组织对象占用的内存。 不同类型的对象,数据及行为均可能不同,因此可以大胆猜测:不同类型的对象由不同的结构体表示

    对象也有一些共性,比如每个对象都需要有一个引用计数,用于实现垃圾回收机制。因此,还可以进一步猜测:表示对象的结构体有一个公共头部

    一. 实例对象的基石—PyObject和PyVarObject

    PyObject和PyVarObject本质上是对象的头部信息

    1.1 PyObject结构体

    Python对象都由PyObject结构体表示,对象引用则是指针PyObject *PyObject结构体定义于头文件object.h,路径为Include/object.h,代码如下

    typedef struct _object {
        _PyObject_HEAD_EXTRA
        Py_ssize_t ob_refcnt;
        struct _typeobject *ob_type;
    } PyObject;

    对结构体中的元素进行说明,

    元素名称 说明
    ob_refcnt 引用计数,对象被其他地方引用时加一,引用解除时减一; 当引用计数为零,便可将对象回收,这是最简单的垃圾回收机制。
    ob_type 类型指针指向对象的类型对象,类型对象描述实例对象的数据及行为。
    _PyObject_HEAD_EXTRA 宏,同样定义在Include/object.h头文件内。

    1.2 宏的定义

    #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
    

    如果Py_TRACE_REFS被定义,宏展开为两个指针ob_nextob_prev用来实现双向链表。注释中说明,双向链表用于跟踪所有活跃堆对象,一般不启用,不深入介绍。

    1.3 PyVarObject结构体

    用于表示变长对象PyVarObject结构体是在PyObject结构体的基础上加入长度信息。

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

    相比object结构体增加了ob_size字段用于记录元素个数。

    1.4 两种头部信息宏定义及其初始化

    具体实例对象视其内存大小是否固定,决定其属于定长对象还是变长对象。相应的需要具有头部信息PyObjectPyVarObject

    因此,头文件准备了两个头部信息的宏定义PyObject_HEADPyObject_VAR_HEAD,方便对象使用,

    #define PyObject_HEAD          PyObject ob_base;
    #define PyObject_VAR_HEAD      PyVarObject ob_base;

    宏定义说明,

    #define PyObject_HEAD PyObject ob_base;
    表示将代码中其他出现PyObject_HEAD的地方,替换成PyObject ob_base;

    1.4.1 定长对象实现

    内存大小固定的浮点数类的实现只需在PyObject头部基础上,用一个双精度浮点数double加以实现,

    typedef struct {
        PyObject_HEAD
    
        double ob_fval;
    } PyFloatObject;

    1.4.2 变长对象实现

    内存大小不固定的列表对象则需要在PyVarObject头部的基础上,用一个动态数组加以实现,数组存储列表包含的对象,即 PyObject 指针,

    typedef struct {
        PyObject_VAR_HEAD
    
        PyObject **ob_item;
        Py_ssize_t allocated;
    } PyListObject;

    PyListObject底层由一个数组实现,关键字段是以下3个,

    字段 说明
    ob_item 指向动态数组的指针,数组保存元素对象指针。
    allocated 动态数组总长度,即列表当前的 容量。
    ob_size 当前元素个数,即列表当前的 长度。

    列表容量不足时,Python会自动扩容,具体机制见list源码解读。

    1.4.3 头部信息宏初始化

    PyObject_HEAD_INIT用于定长对象头部信息初始化。将引用计数ob_refcnt设置为1并将对象类型ob_type设置成给定类型。

    #define PyObject_HEAD_INIT(type)        \
    
        { _PyObject_EXTRA_INIT              \
    
        1, type },

    PyVarObject_HEAD_INIT用于变长对象头部信息初始化。在前者基础上进一步设置长度字段ob_size

    #define PyVarObject_HEAD_INIT(type, size)       \
    
        { PyObject_HEAD_INIT(type) size },

    在源码中经常见到这两个宏定义。

    二. 类型对象的基石—PyTypeObject 2.1 PyTypeObject包含信息

    PyObject记录了Python中所有对象共有的信息。如引用计数、类型指针和变长对象特有的元素个数。但是还有一些细节需要考虑,

    这些作为对象的元信息 ,应该由一个独立实体保存,与对象所属类型密切相关。PyObject中包含的ob_type指针,指向一个类型对象。类型对象PyTypeObject也在Include/object.h中定义,关键字段如下,

    typedef struct _typeobject {
        PyObject_VAR_HEAD
        const char *tp_name; /* For printing, in format "module>.name>" */
        Py_ssize_t tp_basicsize, tp_itemsize; /* For allocation */
    
        /* Methods to implement standard operations */
        destructor tp_dealloc;
        printfunc tp_print;
    
        getattrfunc tp_getattr;
        setattrfunc tp_setattr;
    
        // ...
        /* Attribute descriptor and subclassing stuff */
        struct _typeobject *tp_base;
    
        // ......
    } PyTypeObject;
    

    类型对象PyTypeObject是一个变长对象,包含变长对象头部信息PyObject_VAR_HEAD和专有字段,

    字段 说明
    类型名称 tp_name字段
    类型的继承信息 tp_base字段指向基类对象
    创建实例对象时所需的内存信息 tp_basicsize 和 tp_itemsize 字段
    该类型支持的相关操作信息 tp_print、tp_getattr等函数指针

    PyTypeObject就是类型对象在 Python 中的表现形式,对应着面向对象中“类”的概念。PyTypeObject结构很复杂,目前只需要知道它保存着对象的元信息,描述对象的类型即可。

    2.2 类型对象和实例对象在内存中的关系

    以float为例,考察类型对象和实例对象在内存中的形态和关系,

    >>> float
    class 'float'>
    >>> pi = 3.14
    >>> e = 2.71
    >>> type(pi) is float
    True

    上图的内容并不完全正确,更深入的解读见后一篇博文。

    到此这篇关于Python源码学习之PyObject和PyTypeObject的文章就介绍到这了,更多相关PyObject和PyTypeObject内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

    您可能感兴趣的文章:
    • Python源码解析之List
    • python源码剖析之PyObject详解
    • python opencv人脸识别考勤系统的完整源码
    • python 制作手机归属地查询工具(附源码)
    • python基于tkinter制作无损音乐下载工具(附源码)
    • Python bsonrpc源码解读
    • Python源码学习之PyType_Type和PyBaseObject_Type详解
    • Python制作脚本帮女朋友抢购清空购物车
    上一篇:使用Python pandas读取CSV文件应该注意什么?
    下一篇:pandas数据处理清洗实现中文地址拆分案例
  • 相关文章
  • 

    © 2016-2020 巨人网络通讯 版权所有

    《增值电信业务经营许可证》 苏ICP备15040257号-8

    Python源码学习之PyObject和PyTypeObject Python,源码,学,习之,PyObject,