Python:Python底层原理:Python的整数是如何实现的

这篇具有很好参考价值的文章主要介绍了Python:Python底层原理:Python的整数是如何实现的。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


Python的底层是C/C++ ,但是 C/C++ 能表示的整数范围是有限的,那么Python 是如何做到:整数不溢出的了?
下面我们从以下几个方面来分析下。
  1. Python整数在底层对应的结构体 PyLongObject定义
  2. 整数是怎么存储的
  3. 整数占的内存大小是怎么计算的
  4. 两个整数是怎么比较大小
  5. 整数的加减法运算

1. Python整数在底层对应的结构体 PyLongObject

下面我们来看一下 python3.11 版本源码,它的源码是 C++语言写的

// 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 类型的数组
	digit ob_digit[1];
}PyLongObjcet

根据上述C++存储代码,我们可以得到如下信息:

  1. ob_size 指的是 数组 ob_digit 的长度,而 ob_digit 只能用来维护具体的值,而数组长度的不同,那么对应的整数占用内存也就不同。

💚💚💚
我们再来看下: ob_digit 数组的定义

// ob_digit 定义
longintrepr.h

# if PYLONG_BITS_IN_DIGIT =30 
typedef unint32_t digit;
#elif PYLONG_BITS_IN_DIGIT =15
typedef unsigned short digit;
#endif

  • 由于现在基本都是64位机器,所以只考虑64 位,显然 PYLONG_BITS_IN_DIGIT 会等于30 。 因此 digit 等价于 unint32_t ,也就是 unsigned int ,所以它是一个 无符号32位整型。
  • 因此 ob_digit 是一个无符号 32位 整型数组,长度为1 当然这个数据具体多长则取决于你要存储的整数有多大
  • 数组的长度具体是多少,取决于你的实际整数,显然整数越大,这个数组就越长,占用的空间也就越大。

2.整数是怎么存储的

下面,我们就以 Python整数的底层存储为例,来详细解释下Python底层存储的原则。

2.1 整数0存储

当表示的整数为0时,ob_digit 数组为空,不存储任何值,而 ob_size为0, 表示这个整数的值为0, 这是一种特殊情况。
Python:Python底层原理:Python的整数是如何实现的

2.2 整数1

当存储的值为1时,此时 ob_digit 数组就是 [1] ,显然 ob_size 的值也是 1, 因为 它维护的就是 ob_digit 数组的长度。
Python:Python底层原理:Python的整数是如何实现的

2.3 整数-1

  1. 我们可以看到 ob_digit 数组没有变化,但是 ob_size长度变为 -1, 整数的正负号就是通过这里的 ob_size来决定的
  2. ob_digit 存储的其实是绝对值,无论整数 n 是多少 -n和n 对应的 digit 是完全一致的,但是 ob_size 则刚好相反,所以ob_size 除了表示数组的长度之外,还可以表示对应整数的正负。
  3. 整数越大,底层的数组就越长,更准确的说是绝对值越大,底层数组就越长。
  4. 所以,Python 在比较两个整数的大小时,会先比较 ob_size ,如果 ob_size 不一样则可以直接比较出大小,显然 ob_size越大,对应的整数越大,不管 ob_size 是正是负,都符合这个结论。
    Python:Python底层原理:Python的整数是如何实现的

2.4. 2**30 -1

如果想表示:2**30-1 , 我们直接按照前面三个小节,就可以表示这个整数值,但是为什么需要将这个整数单独拿出来说了 ?
💚💚💚
答案就是:虽然 digit 是 4字节32位,但是解释器 只用了 30位

  1. 如果32个位全部用来存储绝对值,那么相加产生进位的时候,可能会溢出 (如: 2**32-1 此时 32个位全部占满了,即便它只加1,也会溢出),为了解决这个问题:

1:可以先强制转换成 64位整数再进行运算,但是这会以影响效率。
2:但是如果只用30位的话,那么加法就不会溢出。
3:如果数值大到 30个位存不下的话,那么就会多使用一个 digit
Python:Python底层原理:Python的整数是如何实现的

2.5 . 2**30

显然 30个位能表达的最大整数是:230-1 ,它是digit 能存的最大值,而现在 是 230 , 所以数组就要有两个 digit
将两个 digit 的值相加 就可以表示 2**30-1 了。
Python:Python底层原理:Python的整数是如何实现的

2.6 . ob_digit[a, b, c] 对应整数计算

Python:Python底层原理:Python的整数是如何实现的

计算整数所占内存大小

总结

  • 因为底层使用数组存储的,而数组的长度又没限制,所以,理论上就不会溢出。

所以,要想支持任意大小的整数运算,首先要找到适合存放整数的方式,本篇介绍了用 int 数组来存放,当然也可以用字符串来存储,找到合适的数据结构后,要重新定义所有运算操作。

💚💚💚 下面这个思想可以好好想想 。
一个 8 位的无符号整数类型,我们知道它能表示的最大数字是 255,但这时候如果我想表示 256,要怎么办?
可能有人会想,那用两个数来存储不就好了。一个存储 255,一个存储 1,将这两个数放在数组里面。这个答案的话,虽然有些接近,但其实还有偏差:那就是我们并不能简单地按照大小拆分,比如 256 拆分成 255 和 1,要是 265 就拆分成 255 和 10,不能这样拆分。
正确的做法是通过二进制的方式,也就是用新的整数来模拟更高的位。
参考 https://www.jb51.net/article/266697.htm文章来源地址https://www.toymoban.com/news/detail-435848.html

到了这里,关于Python:Python底层原理:Python的整数是如何实现的的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring IOC底层原理实现过程

    Spring IOC(Inversion of Control,控制反转)是 Spring 框架的核心,它的实现原理是基于反射和配置文件实现的。 在 Spring IOC 中,控制权被反转了。传统的应用程序开发中,对象的创建、管理、销毁等全部由应用程序自己来控制,这个过程称为主动管理(Active Management)。而在 Spr

    2024年02月03日
    浏览(32)
  • 微信小程序底层框架实现原理

    小程序(Mini Program)我们都很熟悉,它是一种不用下载安装就能使用的应用,它实现了应用“触手可及”的梦想。如今, 微信已经把小程序打造成了新的开发者生态 ,而小程序也是这么多年来,中国IT行业里为数不多的能够真正影响到普通程序员的创新成果。 在小程序没有

    2024年02月22日
    浏览(30)
  • 【C++】多态的实现及其底层原理

    个人主页:🍝在肯德基吃麻辣烫 我的gitee:gitee仓库 分享一句喜欢的话:热烈的火焰,冰封在最沉默的火山深处。 本文继C++继承之后讲解C++多态。 单单从概念入手不好理解,应该深入理解多态的实现后再回过头来讲解。 现在简单举个例子:我们在购买高铁票时,往往会有成

    2024年02月14日
    浏览(30)
  • Java中的Iterator底层原理实现

    Iterator主要有 两个抽象方法 ,让子类实现。 hasNext ()用来判断还有没有数据可供访问。 next ()方法用于访问集合的下一个数据。 这两个方法不像List的get()那样依赖索引获取数据,也不像Queue的poll方法那样依赖特定规则获取数据。 迭代器的方法将通用性做到了极致, 可以访问

    2023年04月12日
    浏览(22)
  • python中的 collections 模块(用法、详解、底层原理,示例等)

    1.1 defaultdict 功能 可以设置一个默认值作为字典中新key的默认值。该默认值可以是任何对象, 包括函数、列表、元组、集合等。默认值不需要像dict那样事先定义,因为它在需要的时候会自动创建 使用defaultdict,可以简化代码并提高代码的可读性,而且可以防止KeyError异常的出

    2024年04月29日
    浏览(32)
  • Git如何查看分支列表?具体步骤是怎样的?底层原理是什么?

    要查看 Git 中的分支列表,可以使用 git branch 命令。该命令会列出当前仓库中所有的本地分支,并在当前分支前面加上一个星号(*)以标识当前所在的分支。 具体步骤如下: 打开终端或命令行窗口,进入 Git 仓库所在的目录。 运行 git branch 命令,该命令会列出所有本地分支

    2024年02月11日
    浏览(37)
  • 家用电脑如何才能作为公网服务器使用?底层原理是什么?

    想要将家用电脑用作公网服务器,需要进行一系列的操作和设置,包括: 1.申请公网IP地址。公网IP地址是唯一标识一个设备在互联网上的地址,需要向网络服务提供商申请。 2.配置路由器。将家用电脑连接到路由器,并在路由器上设置端口映射,将来自公网的请求转发到家用

    2024年02月11日
    浏览(29)
  • C++:多态的底层实现原理 -- 虚函数表

    目录 一. 多态的原理 1.1 虚函数表 1.2 多态的实现原理 1.3 动态绑定与静态绑定 二. 多继承中的虚函数表 2.1 虚函数表的打印 2.2 多继承中虚函数表中的内容存储情况 对于一个含有虚函数的的类,在实例化出来对象以后,对象所存储的内容包含两部分: 类的成员变量。 一

    2023年04月21日
    浏览(29)
  • 【C++】list的使用及底层实现原理

      本篇文章对list的使用进行了举例讲解。同时也对底层实现进行了讲解。底层的实现关键在于迭代器的实现。希望本篇文章会对你有所帮助。 文章目录 一、list的使用 1、1 list的介绍 1、2 list的使用 1、2、1 list的常规使用  1、2、2 list的sort讲解 二、list的底层实现 2、1 初构l

    2024年02月16日
    浏览(28)
  • 【JUC系列-04】精通Synchronized底层的实现原理

    JUC系列整体栏目 内容 链接地址 【一】深入理解JMM内存模型的底层实现原理 https://zhenghuisheng.blog.csdn.net/article/details/132400429 【二】深入理解CAS底层原理和基本使用 https://blog.csdn.net/zhenghuishengq/article/details/132478786 【三】熟练掌握Atomic原子系列基本使用 https://blog.csdn.net/zhenghuis

    2024年02月09日
    浏览(32)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包