Python源码剖析(三)整数对象 1

Python源码剖析(三)整数对象 1,第1张

Python源码剖析(三)整数对象 1

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; 
} 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 _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

#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
    
    
    static PyLongObject *
    x_add(PyLongObject *a, PyLongObject *b)
    {
    // ......
    }
    

    如何理解 long_as_number 与 PyNumberMethods的关系?

    // object.h
    #ifndef Py_LIMITED_API
    typedef struct {
        
    
        binaryfunc nb_add;
        // .......
    } PyNumberMethods;
    
    // longobject.h
    static PyNumberMethods long_as_number = {
        (binaryfunc)long_add,       
        (binaryfunc)long_sub,       
        0,                          
       // ......
    };
    

    如何通过加法 *** 作理解整型对象时不可变对象(immutable)?

    结合返回值进行考虑

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5710462.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存