PyObject和PyTypeObject

  • 一. 实例对象的基石—PyObject和PyVarObject
    • 1.1 PyObject结构体
    • 1.2 宏的定义
    • 1.3 PyVarObject结构体
    • 1.4 两种头部信息宏定义及其初始化
      • 1.4.1 定长对象实现
      • 1.4.2 变长对象实现
      • 1.4.3 头部信息宏初始化
  • 二. 类型对象的基石—PyTypeObject
    • 2.1 PyTypeObject包含信息
    • 2.2 类型对象和实例对象在内存中的关系

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_EXTRAPy_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_HEADdouble ob_fval;
} PyFloatObject;

1.4.2 变长对象实现

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

typedef struct {PyObject_VAR_HEADPyObject **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_HEADconst 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_basicsizetp_itemsize 字段
该类型支持的相关操作信息 tp_printtp_getattr等函数指针

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

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

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

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

  • 两个float实例对象都是PyFloatObject结构体,除了公共头部字段ob_refcntob_type,专有字段ob_fval保存了对应的数值。
  • 类型对象是一个PyTypeObject结构体,保存了类型名、内存分配信息以及浮点数相关操作。实例对象的ob_type字段指向类型对象,Python 据此判断对象类型,进而获悉关于对象的元信息。
  • float、pi以及e等变量只是一个指向实际对象的指针。

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

PyObject和PyTypeObject相关推荐

  1. python3.7源码分析-集合(set)

    python集合 set是无序且不重复的集合,是可变的,通常用来从列表中删除重复项以及计算数学运算,如交集.并集.差分和对称差分等集合操作.set 支持 x in set, len(set),和 fo ...

  2. Python3.5源码分析-List概述

    Python3源码分析 本文环境python3.5.2. 参考书籍<<Python源码剖析>> python官网 Python3的List对象 list对象是一个变长对象,在运 ...

  3. Python3.5源码分析-垃圾回收机制

    Python3源码分析 本文环境python3.5.2. 参考书籍<<Python源码剖析>> python官网 Python3的垃圾回收概述 随着软硬件的发展,大多数语言都已 ...

  4. Python源码学习:Python类机制分析-用户自定义类

    Python源码分析 本文环境python2.5系列 参考书籍<<Python源码剖析>> 上一文,分析了Python在启动初始化时,对内置类的一个基本的初始化流程,本文就简析 ...

  5. Python源码学习:Python类机制分析

    Python源码分析 本文环境python2.5系列 参考书籍<<Python源码剖析>> 本文主要分析Python中类时如何实现的,在Python中,一切都是对象:任何对象都 ...

  6. python 类实例化理解

    让我们以一个Foo类开始: class Foo(object):def __init__(self, x, y=0):self.x = xself.y = y 当你实例化它(即创建该类的一个新的实例) ...

  7. python deque的内在实现 本质上就是双向链表所以用于stack、队列非常方便

    How collections.deque works? Cosven 前言:在 Python 生态中,我们经常使用 collections.deque 来实现栈.队列这些只需要进行头尾操作的数据结构 ...

  8. 详解 Python 源码之对象机制

    在Python中,对象就是在堆上申请的结构体,对象不能是被静态初始化的,并且也不能是在栈空间上生存的.唯一的例外就是类型对象(type object),Python中所有的类型对象都是被静态初始化的. ...

  9. zg手册 之 python2.7.7源码分析(1)-- python中的对象

    为什么80%的码农都做不了架构师?>>>    源代码主要目录结构 Demo: python 的示例程序 Doc: 文档 Grammar: 用BNF的语法定义了Python的全部语法 ...

最新文章

  1. return与exit()
  2. 一个Java程序员应该掌握的10项技能
  3. kotlin中Int Byte的相互转换,常用
  4. 物化视图常用维护操作
  5. 1.4 满足和优化指标
  6. 利用Redis锁解决高并发问题
  7. 从60%的BI和数据仓库项目失败,看出从业者那些不堪的乱象
  8. 使用BI软件的好处有哪些
  9. 认识协程gevent
  10. 10、斐波那契数列,跳台阶问题(Python)
  11. Python书籍推荐:《Python编程之美:最佳实践指南》
  12. mock和spy的不同点
  13. 笔记本的无线网共享给台式机上网
  14. 从零开始写JavaScript框架
  15. pycharm怎么设置成中文版的
  16. Java 后端工程师的书单推荐,周立功linux开发教程
  17. java及大数据程微信交流群
  18. Allegro焊盘的制作
  19. EXCEL密码VBA解决
  20. 解决Jekins打包报错Failed to execute goal....

热门文章

  1. 信息学奥赛一本通 1348:【例4-9】城市公交网建设问题
  2. 【逆向】Android App soul api-sign算法分析
  3. 思维导图软件 XMind 8 和 XMind 2020的选择
  4. (3)数仓建设-数据仓库设计方案
  5. 12种工作让你摇身变富豪
  6. 5G室内定位来了,化工厂人员定位,电厂室内定位都有用它!-新导智能
  7. 数据结构与算法《二分查找》
  8. switch手柄键位名称图解_Switch官方资料汇总!所有你想知道的都在这里
  9. tor 安装及使用教程
  10. Flutter BlendMode混合模式详解