Python对象底层实现是怎样的,有哪知识点
Admin 2022-08-31 群英技术资讯 267 次浏览
在Python解释器的C层面,一切对象都是以PyObject为基础的
C源码如下:
typedef struct _object { _PyObject_HEAD_EXTRA Py_ssize_t ob_refcnt; PyTypeObject *ob_type; } PyObject;
源码解读:
_PyObject_HEAD_EXTRA:主要用于实现双向链表(分析源码时暂时忽略)
ob_refcnt:引用计数,用于垃圾回收机制,当这个参数减少为0时即代表对象要被删除了(Py_ssize_t当作int或long即可,感兴趣的话可以去看下它的定义)
ob_type:类型指针,指向对象的类型对象(PyTypeObject,稍后介绍),类型对象描述实例对象的数据及行为。如PyLongObject的ob_type指向的就是PyLong_Type
PyVarObject与PyObject相比只多了一个属性ob_size,它指明了边长对象中有多少个元素
C源码如下:
typedef struct { PyObject ob_base; Py_ssize_t ob_size; /* Number of items in variable part */ } PyVarObject;
定长对象和变长对象的大致结构图示如下:
宏定义:对于具体对象,视其大小是否固定,需要包含头部PyObject或PyVarObject,为此,头文件准备了两个宏定义,方便其他对象使用:
#define PyObject_HEAD PyObject ob_base; #define PyObject_VAR_HEAD PyVarObject ob_base;
这里简单的以浮点对象作为定长对象的例子,介绍一下相关概念,后续会详细分析float对象的源码。
对于大小固定的浮点对象,需要在PyObject头部的基础上,用一个双精度浮点数double加以实现:
typedef struct { PyObject_HEAD double ob_fval; } PyFloatObject;
图示如下:
这里简单的以列表对象作为变长对象的例子,介绍一下相关概念,后续会详细分析list对象的源码。
对于大小不固定的列表对象,需要在PyVarObject头部的基础上,用一个动态数组加以实现,数组存储了列表包含的对象的指针,即PyObject指针:
typedef struct { PyObject_VAR_HEAD PyObject **ob_item; Py_ssize_t allocated; } PyListObject;
源码解读:
ob_item:指向动态数组的指针,数组中保存元素对象指针
allocated:动态数组的总长度,即列表当前的“容量”
ob_size:当前元素个数,即列表当前的长度(这里的长度是指:列表包含n个元素,则长度为n)
图示如下:
问题:不同类型的对象所需存储空间不同,创建对象时从哪得知存储信息呢?以及如何判断一个给定对象支持哪些操作呢?
注意到,PyObject结构体中包含一个指针ob_type,指向的就是类型对象,其中就包含了上述问题所需要的信息
C源码如下:(只列出了部分,后续会结合具体类型进行分析)
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 */ PyObject *tp_bases; // ... } PyTypeObject;
源码解读:
PyObject_VAR_HEAD表示PyTypeObject是变长对象
tp_name:类型名称
tp_basicsize、tp_itemsize:创建实例对象时所需的内存信息
tp_print、tp_getattr等:表示该类型支持的相关操作信息
tp_bases:指向基类对象,表示类型的继承信息
PyTypeObject就是类型对象在C层面的表示形式,对应面向对象中”类“的概念,其中保存着对象的”元信息“(即一类对象的操作、数据等)。
下面以浮点类型为例,列出了PyFloatObject和PyTypeObject之间的关系结构图示:(其中两个浮点实例对象都是PyFloatObject结构体,浮点类型对象float是一个PyTypeObject结构体变量)
由于浮点类型对象唯一,在C语言层面作为一个全局变量静态定义即可。C源码如下:(只列出了部分)
PyTypeObject PyFloat_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "float", sizeof(PyFloatObject), 0, (destructor)float_dealloc, /* tp_dealloc */ // ... (reprfunc)float_repr, /* tp_repr */ // ... };
源码解读:
第二行PyVarObject_HEAD_INIT(&PyType_Type, 0):初始化了ob_refcnt、ob_type、ob_sie三个字段,其中ob_type指向了PyType_Type(稍后会继续介绍,它就是type),即:float的类型是type
第三行"float":将tp_name字段初始化为类型名称float
通过PyFloat_Type的ob_type字段,我们找到了type所对应的C语言层面结构体变量:PyType_Type,C源码如下:(只列出了部分)
PyTypeObject PyType_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "type", /* tp_name */ sizeof(PyHeapTypeObject), /* tp_basicsize */ sizeof(PyMemberDef), /* tp_itemsize */ (destructor)type_dealloc, /* tp_dealloc */ // ... (reprfunc)type_repr, /* tp_repr */ // ... };
内建类型和自定义类对应的PyTypeObject对象都是通过这个PyType_Type创建的。在第二行PyVarObject_HEAD_INIT(&PyType_Type, 0)中,PyType_Type把自己的ob_type字段设置成了它自己,即type的类型是type
把PyType_Type加入到结构图中,图示如下:
object是另外一个特殊的类型,它是所有类型的基类。如果要找到object对应的结构体,我们可以通过PyFloat_Type的tp_base字段来寻找,因为它指向的就是float的基类object。但是我们查看源码发现,PyFloat_Type中并没有初始化tp_base字段:
同样地,我们查看Objects文件夹下的各种不同类型所对应的结构体,发现tp_base字段均没有初始化,于是寻找将tp_base字段初始化的函数:
void _Py_ReadyTypes(void) { if (PyType_Ready(&PyBaseObject_Type) < 0) Py_FatalError("Can't initialize object type"); if (PyType_Ready(&PyType_Type) < 0) Py_FatalError("Can't initialize type type"); // ... if (PyType_Ready(&PyFloat_Type) < 0) Py_FatalError("Can't initialize float type"); // ... }
_Py_ReadyTypes中统一调用了PyType_Ready()函数,为各种类型设置tp_base字段:
int PyType_Ready(PyTypeObject *type) { // ... /* Initialize tp_base (defaults to BaseObject unless that's us) */ base = type->tp_base; if (base == NULL && type != &PyBaseObject_Type) { base = type->tp_base = &PyBaseObject_Type; Py_INCREF(base); } // ... }
可以看到,PyType_Ready在初始化tp_base字段时,对于PyBaseObject_Type,不会设置tp_base字段,即object是没有基类的,这就是为了保证继承链有一个终点。
PyBaseObject_Type源码如下:(只列出了部分)
PyTypeObject PyBaseObject_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "object", /* tp_name */ sizeof(PyObject), /* tp_basicsize */ 0, /* tp_itemsize */ object_dealloc, /* tp_dealloc */ // ... object_repr, /* tp_repr */ // ... 0, /* tp_base */ // ... };
源码解读:
第二行PyVarObject_HEAD_INIT(&PyType_Type, 0):把ob_type设置为PyType_Type,即object的类型是type
将PyBaseObject_Type加入到结构图中,图示如下:
object的类型是type,type的基类是object。先有鸡还是先有蛋?
答:
前面我们提到,在各种类型对应的C语言结构体变量初始化的时候,tp_base字段都是没有设置具体值的,直到_Py_ReadyTypes()函数执行时,才通过PyType_Ready()去初始化各类型的tp_base。
在PyBaseObject_Type初始化时,会将ob_tyep字段设置为PyType_Type,即object的类型为type;在_Py_ReadyTypes函数中,会通过PyType_Ready()设置PyType_Type的tp_base字段为PyBaseObject_Type。所以这里本质上不是一个先有鸡还是先有蛋的问题。
PyTypeObject保存元信息:某种类型的实例对象所共有的信息保存在类型对象中,实例对象所特有的信息保存在实例对象中。以float为例:
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。
猜你喜欢
这篇文章主要介绍了Python3时间转换之时间戳转换为指定格式的日期,需要的朋友可以参考下
今天给大家分享的是Python中SortedList类的内容,对大家学习或者巩固python类的基础知识有一定的帮助,有这方面学习需求的朋友可以了解看看,那么接下来就跟随小编来学习一下吧。
这篇文章主要为大家介绍了python上下文管理器使用场景及异常处理示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
这篇文章主要介绍了python实战之利用pygame实现贪吃蛇游戏,文中有非常详细的代码示例,对正在学习python的小伙伴们有很好的帮助哟,需要的朋友可以参考下
Python 中使用线程有两种方式:函数或者用类来包装线程对象。函数式:调用 _thread 模块中的 start_new_thread() 函数来产生新线程。
成为群英会员,开启智能安全云计算之旅
立即注册Copyright © QY Network Company Ltd. All Rights Reserved. 2003-2020 群英 版权所有
增值电信经营许可证 : B1.B2-20140078 粤ICP备09006778号 域名注册商资质 粤 D3.1-20240008