第二章对象模型(2):揭开对象的神秘面纱

前言:上一节中提到了很多的基本概念,是在python层面进行 描述的,总体概念。现在我们就具体了解下

python源码,基于C语言

概念复习

复习一下和要清楚的前面讲的基本概念:

面向对象理论中**“ 类** ”和**“ 对象 ”这两个重要概念,在 Python 内部均以对象**的形式存在。 “类”是一种对象,称为 类型对象“类”实例化生成的“对象”也是对象,称为 实例对象

根据上文描述的,根据对象的不同特点还可以进行分类:
可变对象: 对象创建后可以修改
不可变对象: 对象创建后不能修改
定长对象: 对象大小固定
变长对象: 对象大小不固定

(后面还有可调用对象等等)

C语言实现的底层

  • 首先 python对象在C语言底层 是一个结构体,组织对象占用的内存。 不同类型的对象,数据及行为均可能不同,因此不同类型的对象由不同的结构体表示
  • 此外,对象对比为 的话,所有人都有鼻子眼睛,所以肯定具有一些共性,比如每一个对象都需要引用计数(进行垃圾回收机制),所以,表示对象的结构体室友一个公共的头部

PYObject, 对象的基石

(因为书中有详细的介绍,源码的出处和作用,我们作为学习和理解的笔记,就不去 过多介绍了)

对定长对象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;
  • _PyObject_HEAD_EXTRA 宏,结构体包含以下两个字段

    • 引用计数 ( ob_refcnt ):用于垃圾回收机制,当对象被引用,值加一,引用解除,减一。当引用计数变为零。
    • 类型指针 ( ob_type ):指向对象的类型对象, 类型对象描述实例对象数据及行为(记住这句话,后面介绍)
  • 回过头来看 _PyObject_HEAD_EXTRA 宏的定义,同样在 Include/object.h 头文件内:

#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

我们观察可以发现:

struct _object *_ob_next;
struct _object *_ob_prev;

这两个指针,正是用来实现双向链表的!!,也就是说,复杂的对象室友结构体以及 链表进行连接表示的(后面会有图片理解)

书中原话
结合注释,双向链表用于跟踪所有 活跃堆对象 ,一般不启用,不深入介绍。


对变长对象:PYVarObject:
typedef struct {
    PyObject ob_base;
    Py_ssize_t ob_size; /* Number of items in variable part */
} PyVarObject;

观察可以知道:变长对象比普通对象多一个字段 ob_size用于记录元素个数

如图所示:
在这里插入图片描述

  • 至于具体对象,视其大小是否固定,需要包含头部 PyObject 或 PyVarObject 。所以准备了两个宏定义,方便其他对象调用:
#define PyObject_HEAD          PyObject ob_base;
#define PyObject_VAR_HEAD      PyVarObject ob_base;
  • 下面是具体的例子,用来理解构造对象:
  • (1)浮点对象(上节中提到了是定长对象
typedef struct {
    PyObject_HEAD  // 调用上面的宏定义

    double ob_fval;  // 用一个 双精度浮点数 double 加以实现:
} PyFloatObject;

在这里插入图片描述

  • (2)对于列表(变长对象
typedef struct {
    PyObject_VAR_HEAD  // 使用带ob_size的变长头部

    PyObject **ob_item;
    Py_ssize_t allocated;
} PyListObject;

在这里插入图片描述
可以看出来: PyListObject 底层由一个数组实现,而数组中存储的 明显是对象的指针
ob_item: 指向 动态数组 的指针,数组保存元素对象指针;
allocated: 动态数组总长度,即列表当前的 容量 ;
ob_size: 当前元素个数,即列表当前的 长度 ;
(列表容量不足时, Python 会自动扩容,具体做法书中会在讲解 list 源码时再详细介绍)

  • 最后是说明两个新的用于初始化对象头部的宏定义。(到现在已经提到 还几个关于头部定义的东西,不要迷,搞清楚,他们都是最开始的两个的宏定义,方便使用,后续都是使用下面的两个宏定义用于初始化头部)

    • PyObject_HEAD_INIT 一般用于 定长对象 ,将引用计数 ob_refcnt 设置为 1 并将对象类型 ob_type 设置成给定类型:
#define PyObject_HEAD_INIT(type)        \
    { _PyObject_EXTRA_INIT              \
    1, type },
  • PyVarObject_HEAD_INIT 在 PyObject_HEAD_INIT 基础上进一步设置 长度字段 ob_size ,用于 变长对象
#define PyVarObject_HEAD_INIT(type, size)       \
    { PyObject_HEAD_INIT(type) size },

( 后续都会使用到这两个宏定义,会加深理解 )

总结:Python 中所有对象共有的信息。 对于内存中的任一个对象,不管是何类型,它刚开始几个字段肯定符合我们的预期: 1. 引用计数 、 2. 类型指针 以及变长对象特有的 (3)元素个数

PYTypeObject, 类型基石
下面就是引申书中的问题:
  1. 不同类型的对象所需内存空间不同,创建对象时从哪得知内存信息呢?
  2. 对于给定对象,怎么判断它支持什么操作呢?(上文让记住的地方)
  • 这就是上面提到的 ob_type字段。
    • 对象所支持的操作和 创建他的信息,其实都包含在它的类型中!!。也就是ob_type所指向的类型对象
    • 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 是一个 变长对象, 包含在对象头部。
  • 部分字段:
    • 类型名字:既 tp_name字段
    • 类型的继承信息,例如 tp_base 字段指向基类对象
    • 创建实例对象时所需的 内存信息 ,即 tp_basicsize 和 tp_itemsize 字段
    • 该类型支持的相关 操作信息!! ,即 tp_print 、 tp_getattr 等函数指针;(阅读过python官方文档就知道getattr这些方法,在这里明白在c中的定义,显得格外清晰)

书作者的话:
PyTypeObject 就是 类型对象 在 Python 中的表现形式,对应着面向对象中“类”的概念。 PyTypeObject 结构很复杂,但是我们不必在此刻完全弄懂它。 先有个大概的印象,知道 PyTypeObject 保存着对象的 元信息 ,描述对象的 类型 即可

  • 以浮点类型为例,考察 类型对象 和 实例对象 在内存中的形态和关系
>>> float
<class 'float'>
>>> pi = 3.14
>>> e = 2.71
>>> type(pi) is float
True

代码中各个对象在内存的形式如下图所示:
(这就是涉及到上节提到的,变量只是一个名字!,float也一样,不过是指向了浮点数类型对象)
在这里插入图片描述
其中,两个浮点 实例对象 都是 PyFloatObject 结构体, 除了公共头部字段 ob_refcnt 和 ob_type ,专有字段 ob_fval 保存了对应的数值。 浮点 类型对象 是一个 PyTypeObject 结构体, 保存了类型名内存分配信息以及浮点相关操作。 实例对象 ob_type 字段指向类型对象, Python 据此判断对象类型, 进而获悉关于对象的元信息,如操作方法等。 再次提一遍,float 、 pi 以及 e 等变量只是一个指向实际对象的指针。

  • 由于浮点类型对象全局唯一,所以再C语言层面上,作为一个全局静态定义

在这里插入图片描述
注意到 ob_type 指针指向 PyType_Type ,这也是一个静态定义的全局变量。 由此可见,代表“ 类型的类型 ” 即 type 的那个对象应该就是 PyType_Type 了!!!!

  • 上节中我们知道,类,也就是类型对象,也有自己的类型,也就是 type:
  • C语言中:type的肉身为:PyType_Type ,Object/typeobject.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 创建的!
  • PyType_Type 在 Python 的类型机制中是一个至关重要的对象,它是所有类型的类型,称为 元类型 ( meta class )!! 深入到源码里讲这些,你是否对元类,以及 type有了更深的理解? 哈哈!
  • 另外:第二行,PyType_Type 将自己的 ob_type 字段设置成它自己(第 2 行),这就解释了之前的 type的类型还是 type!
>>> type.__class__
<class 'type'>
>>> type.__class__ is type
True

到这里,我们对 元类型type在对象体系中的位置就非常清晰了!
在这里插入图片描述

再对类型清楚之后,我们就要说下面的 基类: object
PyBaseObject_Type,类型之基
  • 上节里知道:object 是另一个特殊的类型,它是所有类型的基类,我们可以在 PyFloat_Type 中 tp_base 字段找到它!
  • PyBaseObject_Type 就是 object 背后的实体:
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 */
};
  • 这里注意: ob_type 字段指向 PyType_Type 跟 object 在 Python 中的行为时相吻合的! object的 类型也是 type!
>>> object.__class__
<class 'type'>
  • 又注意到 PyType_Ready 函数初始化 PyBaseObject_Type 时,不设置 tp_base 字段。 因为继承链必须有一个终点,不然对象沿着继承链进行属性查找时便陷入死循环(解释上节提到得object继承死循环)
>>> print(object.__base__)
None
现在就已经全部清楚了,并且解释清楚了 object, 类型(class),type三者的关系!

如图:
在这里插入图片描述

总结: 我们在这里,把上节提到的 不同类型的对象,还有实例对象,类型,type,object,都通过源码明白了过来! 理解起来不再是靠死记硬背,真心喜欢这个老师出的这本书!讲解的非常细致,并且易于理解。并且,看不是目的,一定要理解原理,不能死记笔记。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值