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

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

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

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

(0)

相关推荐

  • python抢购软件/插件/脚本附完整源码

    距上篇关于淘宝抢购源码的文章已经过去五个月了,五个月来我通过不停的学习,掌握了更深层的抢购技术及原理,而上篇文章中我仅分享了关于加入购物车的商品的抢购源码,且有部分不足. 博主不提供任何服务器端程序,也不提供任何收费抢购软件.该文章仅作为学习selenium框架及GUI开发的一个示例代码.该思路可运用到其他任何网站,京东,天猫,淘宝均可使用,且不属于外挂或者软件之类,只属于一个自动化点击工具,如有侵犯到任何公司的合法权益,请私信联系,会第一时间将相关代码给予删除. 本篇文章我将附上完整源码,及其

  • python基于tkinter制作无损音乐下载工具(附源码)

    继续写GUI,本次依然使用Tkinter设计一款图形界面,使用Tkinter做一款音乐下载软件,听起来听平常的,但是我这款软件能够下载 无损音乐下载软件,听起来不错吧,Let`s go! 一.准备工作 python Tkinter 二.预览 1.搜索 2.下载 3.结果 无损音乐就这样下载完了. 三.详细设计 这里仅展示我设计的整体思路. 四.源代码 4.1 Music_Search-v1.0.py from tkinter import * from tkinter import ttk fr

  • Python源码解析之List

    一.列表结构体 创建列表C语言底层的结构体 lists = [] list.append('name') list.append('age') list.append('grade') typedef struct{ struct _object *_ob_next; struct _object *_ob_prev; // python内部将对象放在链表进行内存管理 Py_ssize_t ob_refcnt; // 引用计数器,就是多少变量用了它 PyObject **ob_item; //

  • Python源码学习之PyType_Type和PyBaseObject_Type详解

    PyType_Type和PyBaseObject_Type PyObject和PyTypeObject内容的最后指出下图中对实例对象和类型对象的理解是不完全正确的, 浮点类型对象全局唯一,Python在C语言层面实现过程中将其定义为一个全局静态变量,定义于Object/floatobject.c中,命名为PyFloat_Type. PyTypeObject PyFloat_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "float&quo

  • python源码剖析之PyObject详解

    一.Python中的对象 Python中一切皆是对象. ----Guido van Rossum(1989) 这句话只要你学过python,你就很有可能在你的Python学习之旅的前30分钟就已经见过了,但是这句话具体是什么意思呢? 一句话来说,就是面向对象中的"类"和"对象"在Python中都是对象.类似于int对象的类型对象,实现了"类的概念",对类型对象"实例化"得到的实例对象实现了"对象"这个概念.

  • python 制作手机归属地查询工具(附源码)

    Hello,大家好,我来敷衍你们了 [捂脸],今天还是用Tkinter做一个GUI小工具,用于手机归属地查询.我将代码放在了博文中,程序打包好放在蓝奏云. 一.预览 1.启动 2.执行查询 二.源代码 1.GUI from tkinter import * from tkinter import ttk from tkinter import messagebox from Get_Attr import Get_Infos import re import threading class Ap

  • python opencv人脸识别考勤系统的完整源码

    如需安装运行环境或远程调试,可加QQ905733049, 或QQ2945218359由专业技术人员远程协助! 运行结果如下: 代码如下: import wx import wx.grid from time import localtime,strftime import os import io import zlib import dlib # 人脸识别的库dlib import numpy as np # 数据处理的库numpy import cv2 # 图像处理的库OpenCv impo

  • Python bsonrpc源码解读

    bsonrpc 是python中⼀个基于json或bson的远程过程调⽤的库,提供了服务端与客户端实现,其底层采⽤的是基于TCP连接的通信. 程序结构 bsonrpc主要包括以下⽂件: concurrent.py:针对两种并发⽅式(threading线程对象.gevent协程对象)涉及的相应组件(Queue,Event,Lock等)提供统⼀的对外的⽣成接⼝:spawn(),new_promise(),new_queue(), new_lock()等: definitions.py:定义rpc的消

  • Python源码学习之PyObject和PyTypeObject

    前言 Python是C语言实现的,因此Python对象在C语言层面应该是一个结构体 ,组织对象占用的内存. 不同类型的对象,数据及行为均可能不同,因此可以大胆猜测:不同类型的对象由不同的结构体表示. 对象也有一些共性,比如每个对象都需要有一个引用计数,用于实现垃圾回收机制.因此,还可以进一步猜测:表示对象的结构体有一个公共头部. 一. 实例对象的基石-PyObject和PyVarObject PyObject和PyVarObject本质上是对象的头部信息. 1.1 PyObject结构体 Pyt

  • Python内建类型int源码学习

    目录 1 int对象的设计 1.1 PyLongObject 1.2 整数的布局 1.3 小整数静态对象池 1.4 示例 2 大整数运算 2.1 整数运算概述 2.2 大整数运算处理过程 1.long_add()源码: 2.绝对值加法x_add() 3 其他 大整数转float溢出 “深入认识Python内建类型”这部分的内容会从源码角度为大家介绍Python中各种常用的内建类型. 问题:对于C语言,下面这个程序运行后的结果是什么?是1000000000000吗? #include <stdio

  • Python对象的底层实现源码学习

    目录 1. PyObject:对象的基石 2. PyVarObject:变长对象的基础 2.1 浮点对象 2.2 列表对象 3. PyTypeObject:类型的基石 4. PyType_Type:类型的类型 5. PyBaseObject_Type:类型之基 6. 补充 在“Python源码学习笔记:Python万物皆对象”中,我们对Python的对象类型体系有了一定的认识,这篇博客将从源码层面来介绍Python中万物皆对象的底层实现. 1. PyObject:对象的基石 在Python解释器

  • Python内建类型str源码学习

    目录 引言 1 Unicode 2 Python中的Unicode 2.1 Unicode对象的好处 2.2 Python对Unicode的优化 3 Unicode对象的底层结构体 3.1 PyASCIIObject 3.2 PyCompactUnicodeObject 3.3 PyUnicodeObject 3.4 示例 4 interned机制 5 总结 引言 “深入认识Python内建类型”这部分的内容会从源码角度为大家介绍Python中各种常用的内建类型. 在介绍常用类型str之前,在上

  • Python万物皆对象理解及源码学习

    目录 万物皆对象 1 类型对象和实例对象 2 类型.对象体系 2.1 元类型type 2.2 自定义类型 2.3 自定义类型子类 2.4 type和object的关系 3 可变对象与不可变对象 4 变长对象和定长对象 5 补充 万物皆对象 这篇博客的内容主要是针对Python中万物皆对象的理解,对Python的类型.对象体系做一个整体的梳理. 在Python中,一切皆为对象,一个整数是一个对象,一个字符串也是一个对象,基本类型(如int)也是对象.Python不再区别对待基本类型和对象,所有的基

  • Python内建类型float源码学习

    目录 1 回顾float的基础知识 1.1 PyFloatObject 1.2 PyFloat_Type 1.3 对象的创建 1.4 对象的销毁 1.5 小结 2 空闲对象缓存池 2.1 浮点对象的空闲链表 2.2 空闲链表的使用 3 其他 “深入认识Python内建类型”这部分的内容会从源码角度为大家介绍Python中各种常用的内建类型. 1 回顾float的基础知识 1.1 PyFloatObject 1.2 PyFloat_Type C源码(仅列出部分字段): PyTypeObject P

  • Python作用域与名字空间源码学习笔记

    目录 作用域与名字空间 1. 名字绑定 1.1 赋值 1.2 模块导入 1.3 函数.类定义 1.4 as关键字 2. 作用域 2.1 静态作用域 2.2 划分作用域 2.3 闭包作用域 2.4 类作用域 2.5 复杂嵌套 2.5.1 函数嵌套类 2.5.2 类嵌套类 3. 名字空间 3.1 Globals 3.2 Locals 3.3 Enclosings 3.4 Builtin 4. 问题与总结 作用域与名字空间 问题: PI = 3.14 def circle_area(r): retur

  • Python内建类型list源码学习

    目录 问题: 1 常用方法 小结: 题外话: 2 list的内部结构:PyListObject 3 尾部操作和头部操作 3.1 尾部操作 3.2 头部操作 4 浅拷贝和深拷贝 4.1 浅拷贝 4.2 深拷贝 4.3 直接赋值 4.4 小结 个人总结: TODO: 5 动态数组 5.1 容量调整 5.2 append() 5.3 insert() 5.4 pop() 5.5 remove() 6 一些问题 问题: “深入认识Python内建类型”这部分的内容会从源码角度为大家介绍Python中各种

随机推荐