首页 > 编程语言 >Python源码笔记——Python中的整数对象

Python源码笔记——Python中的整数对象

时间:2023-04-07 14:03:30浏览次数:40  
标签:笔记 Python nb Py long PyObject PyLong 源码 tp

1.整数对象

Python3.11.2中,整数结构体叫做PyLongObject

#if PYLONG_BITS_IN_DIGIT == 30
typedef uint32_t digit;
...
#elif PYLONG_BITS_IN_DIGIT == 15
typedef unsigned short digit;
...
#else
#error "PYLONG_BITS_IN_DIGIT should be 15 or 30"
#endif

typedef struct _longobject {
		/* PyObject ob_base;
    Py_ssize_t ob_size; */
    PyObject_VAR_HEAD
    digit ob_digit[1];
} PyLongObject;

通过PyObject_VAR_HEAD我们可以确定,在新版Python中,整形是一个不定长对象。

通过前面的文章,我们知道,对于Python中的对象,与对象相关的元信息实际上都保存在与对象对应的类型对象中,对于PyLongObject,这个类型对象是PyLong_Type

PyTypeObject PyLong_Type = {
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
    "int",                                      /* tp_name */
    offsetof(PyLongObject, ob_digit),           /* tp_basicsize */
    sizeof(digit),                              /* tp_itemsize */
    0,                                          /* tp_dealloc */
    0,                                          /* tp_vectorcall_offset */
    0,                                          /* tp_getattr */
    0,                                          /* tp_setattr */
    0,                                          /* tp_as_async */
    long_to_decimal_string,                     /* tp_repr */
    &long_as_number,                            /* tp_as_number */
    0,                                          /* tp_as_sequence */
    0,                                          /* tp_as_mapping */
    (hashfunc)long_hash,                        /* tp_hash */
    0,                                          /* tp_call */
    0,                                          /* tp_str */
    PyObject_GenericGetAttr,                    /* tp_getattro */
    0,                                          /* tp_setattro */
    0,                                          /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
        Py_TPFLAGS_LONG_SUBCLASS |
        _Py_TPFLAGS_MATCH_SELF,               /* tp_flags */
    long_doc,                                   /* tp_doc */
    0,                                          /* tp_traverse */
    0,                                          /* tp_clear */
    long_richcompare,                           /* tp_richcompare */
    0,                                          /* tp_weaklistoffset */
    0,                                          /* tp_iter */
    0,                                          /* tp_iternext */
    long_methods,                               /* tp_methods */
    0,                                          /* tp_members */
    long_getset,                                /* tp_getset */
    0,                                          /* tp_base */
    0,                                          /* tp_dict */
    0,                                          /* tp_descr_get */
    0,                                          /* tp_descr_set */
    0,                                          /* tp_dictoffset */
    0,                                          /* tp_init */
    0,                                          /* tp_alloc */
    long_new,                                   /* tp_new */
    PyObject_Free,                              /* tp_free */
};

PyLongObject对象的各种操作(比较、运算等)实际上就是调用PyLong_Type中的tp_as_number这个结构体中定义的各种函数指针。

static PyNumberMethods long_as_number = {
    (binaryfunc)long_add,       /*nb_add*/
    (binaryfunc)long_sub,       /*nb_subtract*/
    (binaryfunc)long_mul,       /*nb_multiply*/
    long_mod,                   /*nb_remainder*/
    long_divmod,                /*nb_divmod*/
    long_pow,                   /*nb_power*/
    (unaryfunc)long_neg,        /*nb_negative*/
    long_long,                  /*tp_positive*/
    (unaryfunc)long_abs,        /*tp_absolute*/
    (inquiry)long_bool,         /*tp_bool*/
    (unaryfunc)long_invert,     /*nb_invert*/
    long_lshift,                /*nb_lshift*/
    long_rshift,                /*nb_rshift*/
    long_and,                   /*nb_and*/
    long_xor,                   /*nb_xor*/
    long_or,                    /*nb_or*/
    long_long,                  /*nb_int*/
    0,                          /*nb_reserved*/
    long_float,                 /*nb_float*/
    0,                          /* nb_inplace_add */
    0,                          /* nb_inplace_subtract */
    0,                          /* nb_inplace_multiply */
    0,                          /* nb_inplace_remainder */
    0,                          /* nb_inplace_power */
    0,                          /* nb_inplace_lshift */
    0,                          /* nb_inplace_rshift */
    0,                          /* nb_inplace_and */
    0,                          /* nb_inplace_xor */
    0,                          /* nb_inplace_or */
    long_div,                   /* nb_floor_divide */
    long_true_divide,           /* nb_true_divide */
    0,                          /* nb_inplace_floor_divide */
    0,                          /* nb_inplace_true_divide */
    long_long,                  /* nb_index */
};

3.11.2中,我们可以看到long_add函数不需要进行溢出检查。

#define CHECK_BINOP(v,w)                                \
    do {                                                \
        if (!PyLong_Check(v) || !PyLong_Check(w))       \
            Py_RETURN_NOTIMPLEMENTED;                   \
    } while(0)

static PyObject *
long_add(PyLongObject *a, PyLongObject *b)
{
    CHECK_BINOP(a, b);
    return _PyLong_Add(a, b);
}

PyObject *
_PyLong_Add(PyLongObject *a, PyLongObject *b)
{
    if (IS_MEDIUM_VALUE(a) && IS_MEDIUM_VALUE(b)) {
        return _PyLong_FromSTwoDigits(medium_value(a) + medium_value(b));
    }

    PyLongObject *z;
    if (Py_SIZE(a) < 0) {
        if (Py_SIZE(b) < 0) {
            z = x_add(a, b);
            if (z != NULL) {
                /* x_add received at least one multiple-digit int,
                   and thus z must be a multiple-digit int.
                   That also means z is not an element of
                   small_ints, so negating it in-place is safe. */
                assert(Py_REFCNT(z) == 1);
                Py_SET_SIZE(z, -(Py_SIZE(z)));
            }
        }
        else
            z = x_sub(b, a);
    }
    else {
        if (Py_SIZE(b) < 0)
            z = x_sub(a, b);
        else
            z = x_add(a, b);
    }
    return (PyObject *)z;
}

2.创建

PyObject *
PyLong_FromLong(long ival)

...

PyObject *
PyLong_FromString(const char *str, char **pend, int base)

PyObject *
PyLong_FromUnicodeObject(PyObject *u, int base)

我们通过PyLong_FromLong来了解Python是怎么创建一个PyLongObject对象的。

#define IS_SMALL_INT(ival) (-_PY_NSMALLNEGINTS <= (ival) && (ival) < _PY_NSMALLPOSINTS)
typedef int32_t sdigit;

static PyObject *
get_small_int(sdigit ival)
{
    assert(IS_SMALL_INT(ival));
		// 将值转为索引,例如ival=-5,_PY_NSMALLNEGINTS + -5 == 5 + -5 == 0,即获取small_ints
		// 数组的第一个元素,也就是-5。 
    PyObject *v = (PyObject *)&_PyLong_SMALL_INTS[_PY_NSMALLNEGINTS + ival];
    Py_INCREF(v);
    return v;
}

PyObject *
PyLong_FromLong(long ival)
{
    PyLongObject *v;
    unsigned long abs_ival, t;
    int ndigits;

    /* Handle small and medium cases. */
		// 小整数判断
    if (IS_SMALL_INT(ival)) {
        return get_small_int((sdigit)ival);
    }
    if (-(long)PyLong_MASK <= ival && ival <= (long)PyLong_MASK) {
        return _PyLong_FromMedium((sdigit)ival);
    }

    /* Count digits (at least two - smaller cases were handled above). */
    abs_ival = ival < 0 ? 0U-(unsigned long)ival : (unsigned long)ival;
    /* Do shift in two steps to avoid possible undefined behavior. */
    t = abs_ival >> PyLong_SHIFT >> PyLong_SHIFT;
    ndigits = 2;
    while (t) {
        ++ndigits;
        t >>= PyLong_SHIFT;
    }

    /* Construct output value. */
    v = _PyLong_New(ndigits);
    if (v != NULL) {
        digit *p = v->ob_digit;
        Py_SET_SIZE(v, ival < 0 ? -ndigits : ndigits);
        t = abs_ival;
        while (t) {
            *p++ = (digit)(t & PyLong_MASK);
            t >>= PyLong_SHIFT;
        }
    }
    return (PyObject *)v;
}

3.销毁

前面我们说过,当一个对象不再被引用时,会将引用计数器减1,如果引用计数器等于0,那么就会销毁(如果对象有缓存机制,就会缓存)对象。

针对PyLongObject对象,当最后一次引用PyLongObject的对象销毁时,会触发PyLongObject的销毁。

static inline void Py_DECREF(PyObject *op)
{
    // Non-limited C API and limited C API for Python 3.9 and older access
    // directly PyObject.ob_refcnt.
    if (--op->ob_refcnt == 0) {
        _Py_Dealloc(op);
    }
}
#define Py_DECREF(op) Py_DECREF(_PyObject_CAST(op))

_Py_Dealloc函数中,会调用对象的tp_dealloc函数用于销毁对应对象的内存(*dealloc)(op)

4.缓存机制

Python为避免频繁在堆上申请和释放小整数对象的内存,使用了对象池,用于缓存小整数对象。

默认小整数池的大小为5+257个,区间为[-5, 257),可以通过修改宏定义来修改小整数池的数量。

#define _PY_NSMALLPOSINTS           257
#define _PY_NSMALLNEGINTS           5

// -5 ~ 256 = [-5, 257)
PyLongObject small_ints[_PY_NSMALLNEGINTS + _PY_NSMALLPOSINTS];

Python解释器启动时,会自动初始化小整数池。

/* The following is auto-generated by Tools/scripts/generate_global_objects.py. */
#define _Py_global_objects_INIT { \
    .singletons = { \
        .small_ints = { \
            _PyLong_DIGIT_INIT(-5), \
            _PyLong_DIGIT_INIT(-4), \
						...
				 }, \
				 ...
			}, \
}

以前的Python版本还有free_list用于缓存大整数对象,但是现在整数对象的实现机制改了,去除了大整数对象的缓冲机制。

标签:笔记,Python,nb,Py,long,PyObject,PyLong,源码,tp
From: https://www.cnblogs.com/zzhaolei/p/17295898.html

相关文章

  • Python selenium过图片滑块验证
    计算滑块移动距离defget_distance(image1,image2):'''拿到滑动验证码需要移动的距离:paramimage1:没有缺口的图片对象:paramimage2:带缺口的图片对象:return:需要移动的距离'''#print('size',image1.size)threshold=5......
  • b站下载别人的笔记
    看到有些视频下有别人总结的学习笔记,想打印1.收藏笔记(手机端和电脑端)  2.从我的收藏-专栏导出笔记链接地址(手机端)3.电脑端打开链接ctrl+A全选所有内容右键-打印-另存为PDF ......
  • Python调用TensorFlow时出现:FutureWarning: Passing (type, 1) or '1type' as a synon
    百度了很多说是numpy版本过高,将numpy版本降低即可,我降低为1.16.2但是会碰到pipuninstallnumpy卸载不掉的问题我一直忽视了是在conda创建的虚拟环境acc中运行的这个代码,进入cmdpipuninstall时没注意是在base环境下,还是在自己创建的虚拟环境下,一直uninstall不掉numpy,原因我......
  • C# opc ua客户端实例源码,带ef6+sqlite
    C#opcua客户端实例源码,带ef6+sqlite。代码有完整的注解,及包括所有的链接库和程序结构思维图。纯学习资料YID:2855638904489888......
  • Python 工程动画:将数学和数据带入生活
    Python工程动画:将数学和数据带入生活像电影一样呈现工程数据:在Matplotlib+数学+控制系统中为Python动画创建代码课程英文名:PythonengineeringanimationsBringmath&datatolife此视频教程共8.53GB,中英双语字幕,画质清晰无水印,源码附件全课程地址:https://xueshu.......
  • OpenAI Python API 训练营:学习使用 AI、GPT3 等!
    OpenAIPythonAPI训练营:学习使用AI、GPT3等!使用OpenAI强大的API在项目中生成文本和图像,探索人工智能的力量课程英文名:OpenAIPythonAPIBootcampLearntouseAI,GPT3,andmore!此视频教程共3.24GB,中英双语字幕,画质清晰无水印,源码附件全课程地址:https://xueshu.f......
  • Go-json源码解析
    代码例子如下:typeStudentstruct{Namestring`json:"name"`Ageint`json:"age"`}funcmain(){stu:=Student{Name:"张三",Age:21,}buf:=bytes.NewBuffer(make([]byte,0))//新建一个缓冲区......
  • opencv-python 4.14. 霍夫圆变换
    基础知识铺垫通过检索相关资料,学习到了霍夫圆检测的一点点皮毛知识,它的基本内容是认为图像上任何一个非零像素点,都有可能是一个潜在圆形上的一点。通过投票计算,生成累计坐标平面,然后在设置一个累计权重,去定位圆。在笛卡尔坐标系中圆的方程为(x-a)^2+(y-b)^2=r^2,其中(a,b......
  • c#轻量级高并发物联网服务器接收程序源码
    c#轻量级高并发物联网服务器接收程序源码(仅仅是接收硬件数据程序,没有web端,不是java,协议自己写,如果问及这些问题统统不回复。),对接几万个设备没问题,数据库采用ef6+sqlite,可改ef+MySQL.该程序只是源码使用示例,里面有使用方法,自己研究,难度属中上层不建议新手拿YID:5999612973416375......
  • 低压无感BLDC方波控制,全部源码 ADC方案
    低压无感BLDC方波控制,全部源码,方便调试移植1.通用性极高,图片中的电机,一套参数即可启动。2.ADC方案3.电转速最高12w4.电感法和普通三段式5.按键启动和调速6.开环,速度环,限流环7.参数调整全部宏定义,方便调试代码全部源码YID:2879679186284652......