首页 > 编程语言 >Python源码笔记——Python对象机制的基石【PyObject】

Python源码笔记——Python对象机制的基石【PyObject】

时间:2023-04-07 14:04:16浏览次数:34  
标签:struct Python ob tp PyObject 源码 对象

所有源码均基于Python 3.11.2

1.PyObject定义

// 实际上没有任何东西被声明为PyObject,但是每个指向Python对象的指针都可以转换为PyObject*。
// 这是手动模拟的继承。同样的,每个指向可变大小的Python对象的指针也可以转换为PyObject*,此外,也可以转换为PyVarObject*。
typedef struct _object {
		_PyObject_HEAD_EXTRA  // 定义指针以支持所有活动堆对象的双向链表refchain

		Py_ssize_t ob_refcnt;
    PyTypeObject *ob_type;
} PyObject;

Python通过ob_refcnt字段实现基于引用计数的垃圾回收机制。对于某一个对象A,当有一个新的PyObject*引用A对象时,A的引用计数会增加1,当这个PyObject*引用被删除时,A的引用计数应当减1,当此字段为0时,进行垃圾回收(不一定会释放内存空间,Python中还有缓存机制)。

_PyObject_HEAD_EXTRA是一个宏,当编译Python时指定参数--with-trace-refs,那么Py_TRACE_REFS 会被定义。

#ifdef Py_TRACE_REFS
/* Define pointers to support a doubly-linked list of all live heap objects. */
#define _PyObject_HEAD_EXTRA            \
    PyObject *_ob_next;           \
    PyObject *_ob_prev;

#define _PyObject_EXTRA_INIT _Py_NULL, _Py_NULL,

#else
#  define _PyObject_HEAD_EXTRA
#  define _PyObject_EXTRA_INIT
#endif

ob_type是一个结构体,对应着Python内部的一种特殊的对象,用来指定一个对象类型的类型对象。

2.定长对象和变长对象

在Python中除了PyObject结构体之外,还有PyVarObject结构体。

#define PyObject_VAR_HEAD      PyVarObject ob_base;

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

我们把不包含可变长度数据的对象称为定长对象,而字符串对象、整数对象这样包含可变长度数据的对象称为变长对象。它们的区别在于定长对象的不同对象占用的内存大小是一样的,而变长对象的不同对象占用的内存可能是不一样的。

变长对象通常是容器类型,ob_size这个字段实际上就是指明了变长对象中一共容纳了多少个元素,而不是字节的数量。

  • 为什么整数对象是可变长度对象呢?因为在Python中,整数对象是没有位数限制的,这是一个大整数对象的实现,在整数对象的结构体PyLongObject定义中,使用到了PyVarObject,用于指定size。

3.为什么PyObject要定义在每一个Python对象的最开始字节中

PyVarObject的定义可以看出,PyVarObject只是PyObject的一个扩展而已。因此,对于任何一个PyVarObject,其所占用的内存,开始部分的字节的意义和PyObject是一样的。换句话说,在Python内部,每一个对象都拥有相同的对象头部。这就使得在Python中,对对象的引用变得非常统一,我们只需要用一个PyObject*指针就可以引用任意一个对象,而不论该对象实际是一个什么对象。

这是一个简单的转换示例程序:

#include <stdio.h>

struct PyObject {
    int _ob;
    int _type;
};

struct PyVarObject {
    struct PyObject ob_base;
    int ob_size;
};

// List结构体
struct PyListObject {
    struct PyVarObject ob_var;
    int ob_item;
};

// Python中的双向链表
struct refchain {
    struct PyObject *prev;
    struct PyObject *next;
};

int main() {
    struct PyListObject list =
        {
            .ob_var.ob_size = 2,
            .ob_var.ob_base._ob = 0,
            .ob_var.ob_base._type = 1,
            .ob_item = 3,
        };
    struct PyListObject *p_list = &list;

    // Object和List可以通过指针进行强转
    struct PyObject *p_ob = (struct PyObject *)(p_list);
    printf("Object: %d\n", p_ob->_ob); // Object: 0

    struct PyListObject *p_list1 = (struct PyListObject *)(p_ob);
    printf("List: %d\n", p_list1->ob_item); // List: 3
    return 0;
}

4.类型对象

当在内存中分配空间,创建对象的时候,我们需要知道申请多大的空间。显然,这不是一个定值,因为不同的对象,需要不同的空间,一个整数对象和一个字符串对象所需的空间肯定不同。

PyObject中,PyTypeObject *ob_type字段就表示类型对象。

typedef struct _typeobject {
		PyObject_VAR_HEAD
		const char *tp_name;  /* 用于打印,格式为"<moudle>.<name>" */
		Py_ssize_t tp_basicsize, tp_itemsize;  /* For allocation */

		/* Methods to implement standard operations */
    destructor tp_dealloc;
		...

		/* Method suites for standard classes */

    PyNumberMethods *tp_as_number;
    PySequenceMethods *tp_as_sequence;
    PyMappingMethods *tp_as_mapping;
		
		...

		hashfunc tp_hash;
		ternaryfunc tp_call;
} PyTypeObject;

一个PyTypeObject对象就是Python中面向对象理论中的”类“的实现。

PyTypeObject的定义中,有三组非常重要的操作族,tp_as_number、tp_as_sequence、tp_as_mapping。它们分别指向PyNumberMethods、PySequenceMethods、PyMappingMethods函数族。

typedef PyObject * (*binaryfunc)(PyObject *, PyObject *);

typedef struct {
    binaryfunc nb_add;
    binaryfunc nb_subtract;
    ...
} PyNumberMethods;

PyNumberMethods中,定义了一个数值对象应该支持的操作。

对于一种类型来说,它完全可以同时定义三个函数中的所有操作。即一个对象既可以表现出数值对象的特性,也可以表现出map对象的特性。

>>> class Int(int):
...     def __getitem__(self, key: str) -> str:
...         return key + str(self)
...
>>> a = Int(1)
>>> b = Int(2)
>>> print(a + b)
3
>>> a["key"]
'key1'

a是一个数值类型的对象实例,我们通过重写__getitem__方法,可以视为指定了IntPython内部对应的PyTypeObject对象的tp_as_mapping.mp_subcript操作,最终Int的实例对象可以表现得像map对象一样。原因就是PyTypeObject允许一种类型同时指定三种不同对象的行为特性。

5.引用计数

类型对象是不会被析构的,因为没有人回去增加和减少类型对象的引用计数,Python程序启动后,类型对象就已经定义好了。

在每个对象创建的时候,Python会使用_Py_NewReference(op)宏来将对象的引用计数初始化为1

当对象的引用计数等于0时,Python不一定会将内存空间释放,而是会采用内存缓冲池的机制,将不使用的对象缓存起来,增加Python的执行效率。我们后续介绍Python的缓冲机制。

标签:struct,Python,ob,tp,PyObject,源码,对象
From: https://www.cnblogs.com/zzhaolei/p/17295891.html

相关文章

  • Python源码笔记——Python中的整数对象
    1.整数对象在Python3.11.2中,整数结构体叫做PyLongObject。#ifPYLONG_BITS_IN_DIGIT==30typedefuint32_tdigit;...#elifPYLONG_BITS_IN_DIGIT==15typedefunsignedshortdigit;...#else#error"PYLONG_BITS_IN_DIGITshouldbe15or30"#endiftypedefstruc......
  • Python selenium过图片滑块验证
    计算滑块移动距离defget_distance(image1,image2):'''拿到滑动验证码需要移动的距离:paramimage1:没有缺口的图片对象:paramimage2:带缺口的图片对象:return:需要移动的距离'''#print('size',image1.size)threshold=5......
  • Python调用TensorFlow时出现:FutureWarning: Passing (type, 1) or '1type' as a synon
    百度了很多说是numpy版本过高,将numpy版本降低即可,我降低为1.16.2但是会碰到pipuninstallnumpy卸载不掉的问题我一直忽视了是在conda创建的虚拟环境acc中运行的这个代码,进入cmdpipuninstall时没注意是在base环境下,还是在自己创建的虚拟环境下,一直uninstall不掉numpy,原因我......
  • C# opc ua客户端实例源码,带ef6+sqlite
    C#opcua客户端实例源码,带ef6+sqlite。代码有完整的注解,及包括所有的链接库和程序结构思维图。纯学习资料YID:2855638904489888......
  • Python 工程动画:将数学和数据带入生活
    Python工程动画:将数学和数据带入生活像电影一样呈现工程数据:在Matplotlib+数学+控制系统中为Python动画创建代码课程英文名:PythonengineeringanimationsBringmath&datatolife此视频教程共8.53GB,中英双语字幕,画质清晰无水印,源码附件全课程地址:https://xueshu.......
  • OpenAI Python API 训练营:学习使用 AI、GPT3 等!
    OpenAIPythonAPI训练营:学习使用AI、GPT3等!使用OpenAI强大的API在项目中生成文本和图像,探索人工智能的力量课程英文名:OpenAIPythonAPIBootcampLearntouseAI,GPT3,andmore!此视频教程共3.24GB,中英双语字幕,画质清晰无水印,源码附件全课程地址:https://xueshu.f......
  • Go-json源码解析
    代码例子如下:typeStudentstruct{Namestring`json:"name"`Ageint`json:"age"`}funcmain(){stu:=Student{Name:"张三",Age:21,}buf:=bytes.NewBuffer(make([]byte,0))//新建一个缓冲区......
  • opencv-python 4.14. 霍夫圆变换
    基础知识铺垫通过检索相关资料,学习到了霍夫圆检测的一点点皮毛知识,它的基本内容是认为图像上任何一个非零像素点,都有可能是一个潜在圆形上的一点。通过投票计算,生成累计坐标平面,然后在设置一个累计权重,去定位圆。在笛卡尔坐标系中圆的方程为(x-a)^2+(y-b)^2=r^2,其中(a,b......
  • c#轻量级高并发物联网服务器接收程序源码
    c#轻量级高并发物联网服务器接收程序源码(仅仅是接收硬件数据程序,没有web端,不是java,协议自己写,如果问及这些问题统统不回复。),对接几万个设备没问题,数据库采用ef6+sqlite,可改ef+MySQL.该程序只是源码使用示例,里面有使用方法,自己研究,难度属中上层不建议新手拿YID:5999612973416375......
  • 低压无感BLDC方波控制,全部源码 ADC方案
    低压无感BLDC方波控制,全部源码,方便调试移植1.通用性极高,图片中的电机,一套参数即可启动。2.ADC方案3.电转速最高12w4.电感法和普通三段式5.按键启动和调速6.开环,速度环,限流环7.参数调整全部宏定义,方便调试代码全部源码YID:2879679186284652......