『Python底层原理』--Python整数为什么可以无限大

整数类型是编程中最常见的数据类型之一,但它的实现细节却鲜为人知。

与其他语言不同,Python 的整数是任意精度的,这意味着它们可以无限大,仅受限于内存。

这种特性使得 Python 在处理大整数时非常强大,但也增加了实现的复杂性。

今天,我们将探讨 Python 整数的内部实现,揭示其背后的奥秘。

1. 整数的内部表示

在大多数编程语言中,整数通常是固定精度的,例如 32 位64 位

然而,Python 的整数是任意精度的,这意味着它们可以无限大,而不会出现溢出问题。

这种特性使得 Python在密码学、计算机代数等领域中非常实用。

python 复制代码
# Python 中的整数可以非常大,而不会溢出
big_number = 1234567890123456789012345678901234567890
print(big_number * big_number)  # 输出一个更大的整数

这种任意精度的特性是如何实现的呢?

答案在于 Python 的整数实现方式。

Python 的整数是通过 CPythonPyLongObject 结构体实现的,

这个结构体定义了整数的存储方式,包括符号和数字。

PyLongObject的定义参考:Include/cpython/longintrepr.h 文件。

c 复制代码
typedef struct _PyLongValue {
    uintptr_t lv_tag; /* Number of digits, sign and flags */
    digit ob_digit[1];
} _PyLongValue;

struct _longobject {
    PyObject_HEAD
    _PyLongValue long_value;
};

这里的_longobject就是PyLongObject_PyLongValue中存储了数字的符号和个数。

Python 使用一种**"大基数"**表示法,而不是常见的十进制表示,

64 位 平台上,基数为\(2^{30}\) ,而在 32 位 平台上,基数为\(2^{15}\) 。

64位平台 (基数为 2\^{30} )为例,一个大数据1234567890123456789存储为[1038713109, 76039121, 1]

python 复制代码
def to_digits(n, base=2**30):
    digits = [n % base]

    n = n // base
    while n != 0:
        digits.append(n % base)
        n = n // base

    return digits

x = 1234567890123456789
print(f"整数 {x} 的底层数字表示: {to_digits(x)}")
# 整数 1234567890123456789 的底层数字表示: [1038713109, 76039121, 1]

如果要计算在32位 平台上的表示,只要将传入to_digitsbase参数改为2**15即可。

所以,任意大的整数,在Python内部都用用一个列表来存放,列表中的每个数值都小于 2\^{30} 或者 2\^{15}

2. 整数的内存优化

Python 整数占用较多内存,即使是小整数也需要 28 字节(在 64 位平台上)。

为了优化内存使用,CPython 采取了一些巧妙的策略,尤其是在处理小整数时。

我本机上的Python3.12.4版本中,小于等于 2\^{64} 的整数都是缓存的。

python 复制代码
i = 2**64
j = 2**64
print(f"addr i: {id(i)}, addr j: {id(j)}")
print(f"i 和 j 是否相同: {i is j}")
# addr i: 2595289736288, addr j: 2595289736288
# i 和 j 是否相同: True

i = 2**65
j = 2**65
print(f"addr i: {id(i)}, addr j: {id(j)}")
print(f"i 和 j 是否相同: {i is j}")
# addr i: 2595289736432, addr j: 2595289736480
# i 和 j 是否相同: False

从上面的示例可以看出,当整数 \\le 2\^{64} 时,ij的内存地址是一样的;反之则不一样。

不过,虽然CPython对整数的实现已经很高效了,但是但在处理大量整数时,内存占用仍然是一个需要考虑的问题。

以下是一些优化建议:

  1. 使用array模块或numpy:如果你需要存储大量同类型的整数,使用array模块或numpy会以更紧凑的方式存储数据。
  2. 避免不必要的整数创建:尽量复用已有的整数对象,尤其是在循环中。
  3. 使用生成器:如果只需要逐个处理整数,可以使用生成器而不是创建整个列表。

3. 整数的性能优化

CPython的整数实现不仅考虑了内存使用,还通过多种优化手段提高了运算性能。

  1. 位操作优化 :对于大整数,CPython使用多精度算术,多精度整数在内存中以数组形式存储,每个元素代表一定位数的数值。

关联的源码可参考:Include/cpython/longintrepr.hObjects/longobject.c

  1. 缓存机制优化:对于一些频繁出现的运算或者中间结果,会将其缓存起来。当再次需要这些结果时,直接从缓存中获取,而不是重新计算。

关联的源码可参考:Objects/longobject.cObjects/object.c

  1. 并行计算支持:对于大整数加法,会将计算任务分解成多个子任务,并行地在多个核心上执行。

关联的源码可参考:Python/thread_pthread.hPython/thread_pthread.cObjects/longobject.c

  1. 代码生成优化 :在将整数加法的 Python 代码转换为机器码时,生成更高效的指令序列。

关联的源码可参考:Python/compile.cPython/ceval.c

4. 总结

Python 的整数实现是一个高效且灵活的任意精度整数系统。

通过CPython的源码,我们可以看到Python如何在内部处理大整数,以及如何通过优化策略提高性能和节省内存。

不过,虽然Python的整数实现已经非常强大,但在处理大量数据时,我们仍然可以通过一些技巧进一步优化内存使用和性能。

相关推荐
冷雨夜中漫步6 小时前
Python快速入门(6)——for/if/while语句
开发语言·经验分享·笔记·python
郝学胜-神的一滴7 小时前
深入解析Python字典的继承关系:从abc模块看设计之美
网络·数据结构·python·程序人生
百锦再7 小时前
Reactive编程入门:Project Reactor 深度指南
前端·javascript·python·react.js·django·前端框架·reactjs
喵手8 小时前
Python爬虫实战:旅游数据采集实战 - 携程&去哪儿酒店机票价格监控完整方案(附CSV导出 + SQLite持久化存储)!
爬虫·python·爬虫实战·零基础python爬虫教学·采集结果csv导出·旅游数据采集·携程/去哪儿酒店机票价格监控
2501_944934739 小时前
高职大数据技术专业,CDA和Python认证优先考哪个?
大数据·开发语言·python
helloworldandy9 小时前
使用Pandas进行数据分析:从数据清洗到可视化
jvm·数据库·python
肖永威10 小时前
macOS环境安装/卸载python实践笔记
笔记·python·macos
TechWJ10 小时前
PyPTO编程范式深度解读:让NPU开发像写Python一样简单
开发语言·python·cann·pypto
枷锁—sha10 小时前
【SRC】SQL注入WAF 绕过应对策略(二)
网络·数据库·python·sql·安全·网络安全
abluckyboy11 小时前
Java 实现求 n 的 n^n 次方的最后一位数字
java·python·算法