bilibili视频讲解:https://space.bilibili.com/431392724
用户名:平凡的久月
Python3中用LongObject表示整型Number,Python2中用IntObject和LongObject表示整型Number。
1. PyLongObject 概述
在Python中没有对象能被完全被声明为一个PyObject
,但每一个指向Python
的对象的指针均能够被视为一个PyObject*
指针。
1.1 Python3中一个整型数字占多少个字节?
struct _longobject {
PyObject_VAR_HEAD
digit ob_digit[1];
};
typedef uint32_t digit;
#define PyObject_VAR_HEAD PyVarObject ob_base;
typedef struct {
PyObject ob_base;
Py_ssize_t ob_size; /* Number of items in variable part */
} PyVarObject;
typedef struct _object {
_PyObject_HEAD_EXTRA
Py_ssize_t ob_refcnt;
struct _typeobject *ob_type;
} PyObject;
// 如果定义了Py_TRACE_REFS的话,这个结构将可以支持双向列表,并且所有堆中的活动均在这个列表中
// 因为在 release模式 下没有定义Py_TRACE_REFS,所以可以忽略这个宏或者理解为null
#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_ssize_t,先说结论:Py_ssize_t在64位系统中是64位的int,而32位系统是_W64,所以Py_ssize_t
的本质为一个8个字节的int类型。
// Include/pyport.h
/* Py_ssize_t is a signed integral type such that sizeof(Py_ssize_t) ==
* sizeof(size_t). C99 doesn't define such a thing directly (size_t is an
* unsigned integral type). See PEP 353 for details.
*/
#ifdef HAVE_SSIZE_T
typedef ssize_t Py_ssize_t;
#elif SIZEOF_VOID_P == SIZEOF_SIZE_T
typedef Py_intptr_t Py_ssize_t;
#else
# error "Python needs a typedef for Py_ssize_t in pyport.h."
#endif
关于HAVE_SSIZE_T
,对于HAVE_SSIZE_T
的默认定义为 1 。
关于ssize_t
,即我们默认下,根据平台将ssize_t
的类型设置为__int64
或者_W64
。
// 这两项定义在 PC/pyconfig.h 中,这里非常重要!!!
#ifdef MS_WIN64
typedef __int64 ssize_t;
#else
typedef _W64 int ssize_t;
#endif
#define HAVE_SSIZE_T 1
终上所述,PyLongObject可以等价为下列表达,至少为24个字节。同时·,也可以看出LongObject是一个变长对象(obsize决定)。
struct _longobject {
_PyObject_HEAD_EXTRA // release模式下为null
Py_ssize_t ob_refcnt; // 8个字节
struct _typeobject *ob_type; // 8个字节(指针与系统寻址能力有关,64位系统则为8个字节)
Py_ssize_t ob_size; // 8个字节
digit ob_digit[1]; // 以四个字节为间距增加,可以没有(ob_size=0)
};
1.2 整数对象池
整数对象的缓冲池机制
面向特定对象的缓冲池机制也是Python语言实现时的核心设计策略之一
1.3 一般对象的创建与运行
对于PyLongObject,与对象相关的原信息保存在与对象对应的类型对象
中,其对应PyLong_Type。
整型对象创建过程概括:实例化对象(PyLongObject) --> PyLong_Type(PyTypeObject) --> PyType_Type(PyTypeObject)
先看基本Object
可以看出,各种对象都拥有相同的对象头部,因此只需要一个PyObject *指针就可以引用任意一个对象,而不论该对象实际是一个什么对象。这也就是 PyObject是对象机制基石 的原因。
再看对象的创建过程(以整型对象为例进行一般过程概括,其实整型对象的创建稍有区别,后面会详细说明)
-
调用类型对象的tp_new(int(PyLongObject) --> PyLong_Type(PyTypeObject));
-
如果上述tp_new=null,则根据tp_base找到指定的基类;
-
调用基类中的tp_new(所有类都是以object类为基类的,所以总是能找到一个非空的tp_new);
-
访问类型对象PyLong_Type中的tp_basicsize信息,完成内存申请操作(这个信息记录这一个整数对象应该占用多大内存);
-
调用tp_init完成初始化对象的工作。
最后看看运行时对象与类型的关系
1.4 PyLongObject对象的元信息
根据源码尝试自己回答以下问题
-
两个整数对象如何比较大小?(longobject.c)
-
加法操作如何实现?
// longobject.c /* Add the absolute values of two integers. */ static PyLongObject * x_add(PyLongObject *a, PyLongObject *b) { // ...... }
-
如何理解 long_as_number 与 PyNumberMethods的关系?
// object.h #ifndef Py_LIMITED_API typedef struct { /* Number implementations must check *both* arguments for proper type and implement the necessary conversions in the slot functions themselves. */ binaryfunc nb_add; // ....... } PyNumberMethods; // longobject.h static PyNumberMethods long_as_number = { (binaryfunc)long_add, /*nb_add*/ (binaryfunc)long_sub, /*nb_subtract*/ 0, /*nb_reserved*/ // ...... };
-
如何通过加法操作理解整型对象时不可变对象(immutable)?
结合返回值进行考虑