为了账号安全,请及时绑定邮箱和手机立即绑定
慕课专栏

目录

已到底部

Python 源码深度剖析

原价 ¥ 68.00

立即订阅
04 揭开对象神秘的面纱
更新时间:2020-07-30 09:49:21
我们有力的道德就是通过奋斗取得物质上的成功;这种道德既适用于国家,也适用于个人。——罗素

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

根据对象不同特点还可进一步分类:

类别 特点
可变对象 对象创建后可以修改
不可变对象 对象创建后不能修改
定长对象 对象大小固定
变长对象 对象大小不固定

那么,对象在 Python 内部到底长啥样呢?

由于 Python 是由 C 语言实现的,因此 Python 对象在 C 语言层面应该是一个 结构体 ,组织对象占用的内存。 不同类型的对象,数据及行为均可能不同,因此可以大胆猜测:不同类型的对象由不同的结构体表示。

对象也有一些共性,比如每个对象都需要有一个 引用计数 ,用于实现 垃圾回收机制 。 因此,还可以进一步猜测:表示对象的结构体有一个 公共头部

到底是不是这样呢?——接下来在源码中窥探一二。

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

如果 Py_TRACE_REFS 有定义,宏展开为两个指针,看名字是用来实现 双向链表 的:

struct _object *_ob_next;
struct _object *_ob_prev;

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

对于 变长对象 ,需要在 PyObject 基础上加入长度信息,这就是 PyVarObject

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

变长对象比普通对象多一个字段 ob_size ,用于记录元素个数:

图片描述

定长对象变长对象

至于具体对象,视其大小是否固定,需要包含头部 PyObjectPyVarObject 。 为此,头文件准备了两个宏定义,方便其他对象使用:

#define PyObject_HEAD          PyObject ob_base;
#define PyObject_VAR_HEAD      PyVarObject ob_base;

例如,对于大小固定的 浮点对象 ,只需在 PyObject 头部基础上, 用一个 双精度浮点数 double 加以实现:

typedef struct {
    PyObject_HEAD

    double ob_fval;
} PyFloatObject;

*![PyFloatObject.svg]()*

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

typedef struct {
    PyObject_VAR_HEAD

    PyObject **ob_item;
    Py_ssize_t allocated;
} PyListObject;

图片描述

如图, PyListObject 底层由一个数组实现,关键字段是以下 3 个:

  • 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_INITPyObject_HEAD_INIT 基础上进一步设置 长度字段 ob_size ,一般用于 变长对象

#define PyVarObject_HEAD_INIT(type, size)       \
    { PyObject_HEAD_INIT(type) size },

后续在研读源码过程中,将经常见到这两个宏定义。

PyTypeObject,类型的基石

PyObject 结构体,我们看到了 Python 中所有对象共有的信息。 对于内存中的任一个对象,不管是何类型,它刚开始几个字段肯定符合我们的预期: 引用计数类型指针 以及变长对象特有的 元素个数

随着研究不断深入,我们发现有一些棘手的问题没法回答:

  • 不同类型的对象所需内存空间不同,创建对象时从哪得知内存信息呢?
  • 对于给定对象,怎么判断它支持什么操作呢?

对于我们初步解读过的 PyFloatObjectPyListObject ,并不包括这些信息。 事实上,这些作为对象的 元信息 ,应该由一个独立实体保存,与对象所属 类型 密切相关。

注意到, 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 是一个 变长对象 ,包含变长对象头部。 专有字段有:

  • 类型名称 ,即 tp_name 字段;
  • 类型的继承信息,例如 tp_base 字段指向基类对象;
  • 创建实例对象时所需的 内存信息 ,即 tp_basicsizetp_itemsize 字段;
  • 该类型支持的相关 操作信息 ,即 tp_printtp_getattr 等函数指针;

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

接下来,以 浮点 为例,考察 类型对象实例对象 在内存中的形态和关系:

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

float 为浮点类型对象,系统中只有唯一一个,保存了所有浮点实例对象的元信息。 而浮点实例对象就有很多了,圆周率 pi 是一个,自然对数 e 是另一个,当然还有其他。

代码中各个对象在内存的形式如下图所示:

图片描述

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

由于浮点 类型对象 全局唯一,在 C 语言层面作为一个全局变量静态定义即可,Python 的确就这么做。 浮点类型对象就藏身于 Object/floatobject.c 中, PyFloat_Type 是也:

PyTypeObject PyFloat_Type = {
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
    "float",
    sizeof(PyFloatObject),
    0,
    (destructor)float_dealloc,                  /* tp_dealloc */

    // ...
    (reprfunc)float_repr,                       /* tp_repr */

    // ...
};

其中,第 2 行初始化 ob_refcntob_type 以及 ob_size 三个字段; 第 3 行将 tp_name 字段初始化成类型名称 float ;再往下是各种操作的函数指针。

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

PyType_Type,类型的类型

我们初步考察了 float 类型对象,知道它在 C 语言层面是 PyFloat_Type 全局静态变量。 类型是一种对象,它也有自己的类型,也就是 Python 中的 type

>>> float.__class__
<class 'type'>

自定义类型也是如此:

>>> class Foo(object):
...     pass
...
>>> Foo.__class__
<class 'type'>

那么, typeC 语言层面又长啥样呢?

围观 PyFloat_Type 时,我们通过 ob_type 字段揪住了 PyType_Type 。 的确,它就是 type 的肉身。 PyType_TypeObject/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_TypePython 的类型机制中是一个至关重要的对象,它是所有类型的类型,称为 元类型 ( meta class )。 借助元类型,你可以实现很多神奇的高级操作。

注意到, PyType_Type 将自己的 ob_type 字段设置成它自己(第 2 行),这跟我们在 Python 中看到的行为是吻合的:

>>> type.__class__
<class 'type'>
>>> type.__class__ is type
True

至此,元类型 type 在对象体系里的位置非常清晰了:

图片描述

PyBaseObject_Type,类型之基

object 是另一个特殊的类型,它是所有类型的基类。 那么,怎么找到它背后的实体呢? 理论上,通过 PyFloat_Typetp_base 字段顺藤摸瓜即可。

然而,我们发现这个字段在并没有初始化:

0,                                          /* tp_base */

这又是什么鬼?

接着查找代码中 PyFloat_Type 出现的地方,我们在 Object/object.c 发现了蛛丝马迹:

if (PyType_Ready(&PyFloat_Type) < 0)
    Py_FatalError("Can't initialize float type");

敢情 PyFloat_Type 静态定义后还是个半成品呀! PyType_Ready 对它做进一步加工,将 PyFloat_Typetp_base 字段初始化成 PyBaseObject_Type

int
PyType_Ready(PyTypeObject *type)
{
    // ...

    base = type->tp_base;
    if (base == NULL && type != &PyBaseObject_Type) {
        base = type->tp_base = &PyBaseObject_Type;
        Py_INCREF(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_TypeobjectPython 中的行为时相吻合的:

>>> object.__class__
<class 'type'>

又注意到 PyType_Ready 函数初始化 PyBaseObject_Type 时,不设置 tp_base 字段。 因为继承链必须有一个终点,不然对象沿着继承链进行属性查找时便陷入死循环。

>>> print(object.__base__)
None

至此,我们完全弄清了 Python 对象体系中的所有实体以及关系,得到一幅完整的图画:

图片描述

虽然很多细节还没来得及研究,这也算是一个里程碑式的胜利!让我们再接再厉!

立即订阅 ¥ 68.00

你正在阅读课程试读内容,订阅后解锁课程全部内容

精选留言 27
欢迎在这里发表留言,作者筛选后可公开显示
  • emmm应该说cpython是c实现的哥哥
    1
    回复
    2021-11-12
  • 之前几个月啃《python源码剖析》,还有点云里雾里,看了老师的课通透了许多。现在在网易互娱工作,向前辈学习!
    0
    回复
    2021-10-11
  • 老师,图是用什么工具画的啊
    1
    回复
    2021-10-09
  • 请问下老师,_PyObject_EXTRA_INIT这个和垃圾回收有关系吗,我看到最后一章提到会在对象头部之前预留了一些内存空间,以便垃圾回收模块用链表将它们跟踪起来。和这一章描述的_PyObject_EXTRA_INIT有点相似,请问这两者有什么关系吗?
    0
    回复
    2021-10-06
  • 从元类来看,类都是通过 type 创建的,这是怎么体现的?而且内置类早就创建好了,创建跟 type 也没多大关系。
    0
    回复
    2021-05-21
    • 内置类型对象,以PyFloat_Type为例,是静态定义的,ob_type字段在定义的时候就指定好了。这样就跟type联系起来。由于创建过程为静态定义,不需要通过type分配内存,也不需要通过type进行初始化。这相当于在type体系之外,进行特殊处理,但仍与type关联,以保持统一。
      回复
      2021-07-05 09:24:20
    • 对的,其他留言中有讨论过。
      回复
      2021-12-14 17:55:09
  • 您好,我想问一下,在else里面 #define _PyObject_HEAD_EXTRA 后面没有常量,那这个定义的目的是什么,作用又是什么呢
    0
    回复
    2021-03-30
    • 如果定义了Py_TRACE_REFS,就将_PyObject_HEAD_EXTRA定义为两个指针字段,目的是启用追踪链表;else如果Py_TRACE_REFS未定义,就将_PyObject_HEAD_EXTRA定义为空,也就是不引入这两个指针字段,不启用追踪链表。如果不将_PyObject_HEAD_EXTRA定义为空,对象定义中引用_PyObject_HEAD_EXTRA会报错。这些是C语言宏定义的基本语法,可以看一些C语言编程书,会有详细介绍。
      回复
      2021-03-31 13:21:39
  • 我觉得这68块钱是我用python三四年以来花在学习上最!踏!马!值得的钱,sorry爆粗口额
    16
    回复
    2021-02-23
  • 讲的真的是太好了.看到这个课程但试读了一段后,直接买了.一口气读到现在.对于复杂的底层逻辑能用简单的语言和图讲解清楚很不容易.提一点小建议,在讲解PyType_Type时,可以再讲一下PyTypeObject和Pytype_Type的关系.看了几遍以后还是不能完全理解.
    1
    回复
    2021-02-22
    • PyTypeObject是一个结构体,所有类型对象底层都是用这个结构体来保存的,它只规定了底层内存结构,即:类型对象都有哪些字段? PyType_Type是PyTypeObject结构体的一个变量,也就是type类型对象的肉身; PyFloat_Type也是PyTypeObject结构体的一个变量,也就是float类型对象的肉身。
      回复
      2021-02-23 13:19:05
    • 后续我再加一张图示,来突出PyTypeObject和PyType_Type以及PyFloat_Type的关系,应该会更清晰些。
      回复
      2021-02-23 13:33:39
  • 老师好.在对定长object初始化时,文章中说将引用技术ob_refcnt设置为1.但代码中设置但并不是ob_refcnt,而是_PyObject_EXTRA_INIT这个变量.这是为什么呢?
    0
    回复
    2021-02-22
    • _PyObject_EXTRA_INIT是一个宏定义,用来初始化两个链表指针。这个链表是用来跟踪堆中的活跃对象用的,如果开启的这个功能的话,在对象的最开头,会有两个链表指针。_PyObject_EXTRA_INIT负责将这两个指针初始化为0,也就是NULL。 _PyObject_EXTRA_INIT之后,就是初始化ob_refcnt和ob_type。
      回复
      2021-02-22 10:07:14
  • 老师您好,读了您的专利,受益匪浅。有个问题希望您能帮忙解答一下。一直有个困惑,一些python的内置函数或是内置方法,是用c写的,在调用这些函数时,是如何调用的c的代码,这之间是如何连接起来的。期待您的回复。
    0
    回复
    2021-01-28
    • Python虚拟机是用C语言编写的,可以直接调用C语言函数,没有任何障碍。很多内建函数是由C语言编写的,模块对象保存了这些C函数的指针。当Python调用这些函数时,CALL_FUNCTION字节码会找到这些函数指针并调用它们。
      回复
      2021-02-01 10:19:11
  • 老师您好,文中这一段:“其中,第 2 行初始化 ob_refcnt 、 ob_type 以及 ob_size 三个字段; 第 3 行将 tp_name 字段初始化成类型名称 float ;再往下是各种操作的函数指针。“ 不是很理解,PyVarObject_HEAD_INIT(&PyType_Type, 0) 这一行实际执行的是下面这一段初始化吗?没有看到ob_refcnt 、 ob_type 以及 ob_size 三个字段 /* PyObject_HEAD defines the initial segment of every PyObject. */ #define PyObject_HEAD PyObject ob_base; #define PyObject_HEAD_INIT(type) \ { _PyObject_EXTRA_INIT \ 1, type }, #define PyVarObject_HEAD_INIT(type, size) \ { PyObject_HEAD_INIT(type) size },
    1
    回复
    2021-01-17
    • 这个是C语言的宏定义展开,PyVarObject_HEAD_INIT(&PyType_Type, 0),被展开成: { PyObject_HEAD_INIT(&PyType_Type) 0 },
      回复
      2021-01-18 13:37:35
    • 接着被展开成:{ { _PyObject_EXTRA_INIT 1, type} 0 },
      回复
      2021-01-18 13:40:16
    • 上一条应该是:{ { _PyObject_EXTRA_INIT 1, &PyType_Type} 0 },
      回复
      2021-01-18 13:41:20
    点击展开后面 1
  • 这个专栏太棒了!居然没有早点发现!之前读源码的一些疑惑都茅塞顿开!
    3
    回复
    2021-01-08
  • 老师您好 "内建类型和自定义类对应的 PyTypeObject 对象都是这个通过 PyType_Type 创建的" 关于这段话的理解: 1. 首先,这段话的概念如同:"float type" is an instance of type 2. 但是从源码上看, 这些类型对象,都是通过PyTypeObject 结构体来创建的。 PyTypeObject PyFloat_Type {...}; PyTypeObject PyType_Type {...}; 那么,从源码上我不是很能理解,这些 类型对象 是通过PyType_Type创建的 这个概念, 麻烦老师解答一下,谢谢
    0
    回复
    2020-12-27
    • 所有的类型对象,包括内建类型、自定义类还有元类,本质上都是类型对象,底层结构是类型的,内存结构由PyTypeObject结构体定义。通过PyType_Type创建的意思是,以自定义类型为例,调用type,type将为新的自定义类分配内存(PyTypeObject结构体)并初始化相关字段;以内建对象为例,由于内建类型都是静态定义,type无须为其分配内存,但仍负责字段初始化。
      回复
      2020-12-28 14:51:58
  • PyTypeObject PyType_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) // ... }; 老师好,问个C语言相关的问题,为什么PyType_Type还没赋值,就可以引用取地址?
    0
    回复
    2020-12-27
    • 变量只要申明就会分配内存空间,这时就可以引用它的地址;而赋值其实是对变量的内存进行初始化。无论变量是否已经完成初始化,它的内存空间总是存在的。
      回复
      2020-12-28 09:59:20
  • 非常清晰的讲解,谢谢
    0
    回复
    2020-12-09
  • 老师好,PyTypeObject、PyType_Type、PyFloat_Type之间的关系没看太明白,能再解释一下吗。我的理解是PyType_Type对应了元类,PyFloat_Type对应了float类对象,那PyTypeObject又是什么呢?看到PyObject 的ob_type指向的是PyTypeObject呀,难道PyType_Type和PyFloat_Type是PyTypeObject的变体吗?
    0
    回复
    2020-12-05
    • PyTypeObject是C语言中的结构体,结构体定义变量的内存布局;PyType_Type和PyFloat_Type都是PyTypeObject类型的变量;也就是说PyType_Type和PyFloat_Type底层存储结构是一样,它们都有一样的字段,但字段值不一样。
      回复
      2020-12-07 17:34:46
    • 明白了。谢谢老师
      回复
      2020-12-08 11:25:23
  • #define PyObject_HEAD PyObject ob_base;这个怎么理解?
    1
    回复
    2020-08-20
    • 这是一个宏定义,表示将代码中其他出现PyObject_HEAD的地方,替换成PyObject ob_base;。
      回复
      2020-08-21 10:47:15
  • 老师好,我想知道python对象的元数据都包含哪些内容, 因为阅读了下一节,我发现还包含对象创建的信息,我想知道它具体包含那些信息
    0
    回复
    2020-08-06
    • PyTypeObject中定义的都是。由于这个结构体很大,文中只提及若干个典型的字段,其他都做了忽略,不过应该不影响阅读。总而言之,对象的元数据保存在它的类型对象中,而类型对象底层是一个PyTypeObject结构体,元数据就保存在结构体字段中。
      回复
      2020-08-07 11:35:45
  • 找这种源码剖析的相关资料好久了,真心好东西
    5
    回复
    2020-08-02
  • 老师,请问下配图用什么画图工具制作的,我自己写博客也想画出这样好看的图。
    0
    回复
    2020-07-29
    • 同学你好,课程中老师使用的插图工具是:lucidchart。感谢你的反馈,祝你学习愉快
      回复
      2020-07-30 09:45:19
  • 那么3.14在创建的时候只创建了ob_refcnt、ob_type、3.14,这一层的内存空间,而计算内存占用的时候也只是计算这一层的,对不对?还有一个问题不太理解,PyFloatType作为类型对象,我理解应该是固定长度的,那为什么头部信息不是PyObject,而是PyVarObject呢。希望老师解惑
    0
    回复
    2020-07-23
    • 亲对第一个问题的理解是完全正确的。至于第二个问题,float是类型对象,是type的实例,底层由PyType_Type结构体表示,即静态变量PyFloatType。PyType_Type还可以表示其他类型对象,比如int str ,甚至是自定义类等等。而不同的类型,它定义的属性、方法肯定有诸多差异。因此,PyType_Type必须是变长的,视不同类型,保存不同的信息。需要特别注意,PyType_Type是一个结构体定义,而PyFloatType该结构体的变量。
      回复
      2020-07-27 11:44:09
    • ”需要特别注意,PyType_Type是一个结构体定义,而PyFloatType该结构体的变量。“ PyTypeObject 是 C 语言中的结构体,结构体定义变量的内存布局;PyType_Type 是 PyTypeObject 类型的变量,结构体的的变量还能继续当作结构体定义吗?
      回复
      2021-11-22 00:25:06
    • 不太明白你的问题
      回复
      2021-12-14 17:59:37
    点击展开后面 1
  • 老师您好,我在自己的linux系统上找半天都没找到您说的Object/文件夹,这个文件夹应该是在哪里能找到呢,谢谢~
    1
    回复
    2020-07-22
    • 是啊,我也想知道在哪里找到那些`.c`源码文件,我只能在`\include\`找到`.h`文件
      回复
      2020-07-23 17:24:09
    • 我发现可以去github上下载cpython源代码,能够找到Object文件夹以及其他代码文件
      回复
      2020-07-23 17:56:35
    • 源码可以从Python官网下载,专栏引用的源码以从官网下载的 3.7.4 版本为准,其他版本可能有些许差异。具体可以参考这篇简介:https://python.fasionchan.com/zh_CN/latest/source/preface/source.html 。
      回复
      2020-07-27 11:32:31
  • 吃着饭的时间读完了,太厉害了~
    0
    回复
    2020-07-22
  • PyTypeObject 应该是具体负责在内存中创建实例,但是它如何保存这些元信息呢?一直以为对象的元信息比如所占内存大小,支持的操作等属性和行为是来自类型对象的定义但应该还是由实例对象自身进行存储,所以这里有些看不懂
    2
    回复
    2020-07-14
    • 某种类型实例对象所共有的信息保存在类型对象中,实例对象所特有的信息保存在实例对象。举个最简单的例子,float对象,不管是3.14还是2.71,都支持加法操作+,因此加法处理函数的指针就保存在类型对象,也就是float中;数值3.14是某个实例对象所特有的,与其他实例对象有区别,2.71也是,因此具体数值在底层作为一个double类型字段保存在实例对象中。编程其实是对现实世界进行建模的过程,归纳共同点与差异点,然后抽象成不同层次且相互关联的实体。
      回复
      2020-07-14 09:54:18
  • 哇,一图胜千言啊
    10
    回复
    2020-06-16
  • 酷哦!老师辛苦了,图画很形象,对于理解很有帮助。
    4
    回复
    2020-05-22
    • 谢谢支持,你们的满意是我创作的动力!
      回复
      2020-05-25 10:57:52
  • 我不太看懂c语言写的代码,开始阅读本专栏之前,要有c语言基础吗?
    0
    回复
    2020-05-19
    • 没有 C 语言基础不要紧,有基础则更佳。你只需看懂一些简单的结构体定义即可,诸如本节介绍的这些。本专栏力求降低阅读难度,不会深陷 C 语言细节,主要以形象的结构图示,配以通俗的语言展开讲解。因此,只要你能独立编写 Python 程序,对 Python 虚拟机运行原理有好奇心,对设计高效 Python 程序有强烈追求,便可加入学习。
      回复
      2020-05-21 10:27:59

点击展开剩余评论

千学不如一看,千看不如一练

手机
阅读

扫一扫 手机阅读

Python 源码深度剖析
立即订阅 ¥ 68.00

举报

0/150
提交
取消