• Python的整数是如何实现


    本次我想聊一聊 Python 的整数,我们知道 Python 的整数是不会溢出的,换句话说,它可以计算无穷大的数,只要你的内存足够,它就能计算。

    而 C 显然没有这个特征,C 里面能表示的整数范围是有限的。但问题是,Python 底层又是 C 实现的,那么它是怎么做到整数不溢出的呢?既然想知道答案,那么看一下整数在底层是怎么定义的就行了。

    整数的底层实现

    Python 整数在底层对应的结构体是 PyLongObject,我们看一下具体的定义,这里的源码版本为最新的 3.11。

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    // Include/cpython/longintrepr.h

    struct _longobject {

        PyObject_VAR_HEAD

        digit ob_digit[1];

    };

    // Include/pytypedefs.h

    typedef struct _longobject PyLongObject;

    // 将两者合起来可以看成

    typedef struct {

        PyObject_VAR_HEAD

        digit ob_digit[1];

    } PyLongObject;

    // 如果把这个PyLongObject 更细致的展开一下

    typedef struct {

        // 引用计数  

        Py_ssize_t ob_refcnt; 

        // 类型

        struct _typeobject *ob_type; 

        // 维护的元素个数

        Py_ssize_t ob_size;

        // digit 类型的数组,长度为 1 

        digit ob_digit[1]; 

    } PyLongObject;

    别的先不说,就冲里面的 ob_size 我们就可以思考一番。首先 Python 的整数有大小、但应该没有长度的概念吧,那为什么会有一个 ob_size 呢?

    从结构体成员来看,这个 ob_size 指的应该就是数组 ob_digit 的长度,而这个 ob_digit 显然只能是用来维护具体的值了。而数组的长度不同,那么对应的整数占用的内存也不同。

    所以答案出来了,整数虽然没有我们生活中的那种长度的概念,但它是个变长对象,因为不同的整数占用的内存可能是不一样的。因此这个 ob_size 指的是底层数组的长度,因为整数对应的值在底层是使用数组来存储的。尽管它没有字符串、列表那种长度的概念,或者说无法对整数使用 len 函数,但它是个变长对象。

    那么下面的重点就在这个 ob_digit 数组身上了,我们要从它的身上挖掘信息,看看一个整数是怎么放在这个数组里面的。不过首先我们要搞清楚这个 digit 是什么类型,它的定义同样位于 longintrepr.h 中:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    // PYLONG_BITS_IN_DIGIT是一个宏

    // 至于这个宏是做什么的我们先不管

    // 总之,如果机器是 64 位的,那么它会被定义为 30

    // 机器是 32 位的,则会被定义为 15

    #if PYLONG_BITS_IN_DIGIT == 30

    typedef uint32_t digit;

    // ...

    #elif PYLONG_BITS_IN_DIGIT == 15

    typedef unsigned short digit;

    // ...

    #endif

    由于现在基本上都是 64 位机器,所以我们只考虑 64 位,显然 PYLONG_BITS_IN_DIGIT 会等于 30。因此 digit 等价于 uint32_t,也就是 unsigned int,所以它是一个无符号 32 位整型。

    因此 ob_digit 是一个无符号 32 位整型数组,长度为 1。当然这个数组具体多长则取决于你要存储的整数有多大,不同于 Golang,C 数组的长度不属于类型信息。

    虽然定义的时候,声明数组的长度为 1,但你可以把它当成任意长度的数组来用,这是 C 语言中常见的编程技巧。至于长度具体是多少,则取决于你的整数大小。显然整数越大,这个数组就越长,占用的空间也就越大。

    搞清楚了 PyLongObject 里面的所有成员,那么下面我们就来分析 ob_digit 是怎么存储整数的,以及 Python 的整数为什么不会溢出。

    不过说实话,关于整数不会溢出这个问题,相信很多人已经有答案了。因为底层是使用数组存储的,而数组的长度又没有限制,所以当然不会溢出。

    来源:https://www.weidianyuedu.com

  • 相关阅读:
    设计通用流程和可变点的方法一些思考
    【C++】常用拷贝和替换算法
    Kotlin读书笔记之优雅且高效的Kotlin
    Redis实现并发阻塞锁方案
    QTcpSocket发送结构体的做法
    Java抽象类快速入门
    基于JavaWeb的企业公司管理系统设计与实现
    Python入门学习篇(一)——注释&变量&输入输出
    关于Semaphore信号量的源码解读
    入门JavaWeb之 Response 下载文件
  • 原文地址:https://blog.csdn.net/hdxx2022/article/details/128200081