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

打印 上一主题 下一主题

主题 903|帖子 903|积分 2709

整数范例是编程中最常见的数据范例之一,但它的实现细节却鲜为人知。
与其他语言不同,Python 的整数是恣意精度的,这意味着它们可以无限大,仅受限于内存。
这种特性使得 Python 在处置惩罚大整数时非常强大,但也增加了实现的复杂性。
今天,我们将探讨 Python 整数的内部实现,揭示其背后的奥秘。
1. 整数的内部表现

在大多数编程语言中,整数通常是固定精度的,例如 32 位64 位
然而,Python 的整数是恣意精度的,这意味着它们可以无限大,而不会出现溢出问题。
这种特性使得 Python在密码学、盘算机代数等范畴中非常实用。
  1. # Python 中的整数可以非常大,而不会溢出
  2. big_number = 1234567890123456789012345678901234567890
  3. print(big_number * big_number)  # 输出一个更大的整数
复制代码
这种恣意精度的特性是如何实现的呢?
答案在于 Python 的整数实现方式。
Python 的整数是通过 CPython 的   PyLongObject   布局体实现的,
这个布局体界说了整数的存储方式,包括符号和数字。
PyLongObject的界说参考:Include/cpython/longintrepr.h 文件。
  1. typedef struct _PyLongValue {
  2.     uintptr_t lv_tag; /* Number of digits, sign and flags */
  3.     digit ob_digit[1];
  4. } _PyLongValue;
  5. struct _longobject {
  6.     PyObject_HEAD
  7.     _PyLongValue long_value;
  8. };
复制代码
这里的_longobject就是PyLongObject,_PyLongValue中存储了数字的符号和个数。
Python 使用一种“大基数”表现法,而不是常见的十进制表现,
64 位平台上,基数为\(2^{30}\) ,而在 32 位平台上,基数为\(2^{15}\) 。
64位平台(基数为$ 2^{30} $)为例,一个大数据1234567890123456789存储为[1038713109, 76039121, 1]。
  1. def to_digits(n, base=2**30):
  2.     digits = [n % base]
  3.     n = n // base
  4.     while n != 0:
  5.         digits.append(n % base)
  6.         n = n // base
  7.     return digits
  8. x = 1234567890123456789
  9. print(f"整数 {x} 的底层数字表示: {to_digits(x)}")
  10. # 整数 1234567890123456789 的底层数字表示: [1038713109, 76039121, 1]
复制代码
假如要盘算在32位平台上的表现,只要将传入to_digits的base参数改为2**15即可。
以是,恣意大的整数,在Python内部都用用一个列表来存放,列表中的每个数值都小于$ 2^{30} $ 或者$ 2^{15} $ 。
2. 整数的内存优化

Python 整数占用较多内存,即使是小整数也必要 28 字节(在 64 位平台上)。
为了优化内存使用,CPython 接纳了一些巧妙的策略,尤其是在处置惩罚小整数时。
我本机上的Python3.12.4版本中,小于等于$ 2^{64} $的整数都是缓存的。
  1. i = 2**64
  2. j = 2**64
  3. print(f"addr i: {id(i)}, addr j: {id(j)}")
  4. print(f"i 和 j 是否相同: {i is j}")
  5. # addr i: 2595289736288, addr j: 2595289736288
  6. # i 和 j 是否相同: True
  7. i = 2**65
  8. j = 2**65
  9. print(f"addr i: {id(i)}, addr j: {id(j)}")
  10. print(f"i 和 j 是否相同: {i is j}")
  11. # addr i: 2595289736432, addr j: 2595289736480
  12. # i 和 j 是否相同: False
复制代码
从上面的示例可以看出,当整数$ \le 2^{64} $时,i和j的内存地址是一样的;反之则不一样。
不过,虽然CPython对整数的实现已经很高效了,但是但在处置惩罚大量整数时,内存占用仍然是一个必要考虑的问题。
以下是一些优化发起:

  • 使用array模块或numpy:假如你必要存储大量同范例的整数,使用array模块或numpy会以更紧凑的方式存储数据。
  • 避免不必要的整数创建:尽量复用已有的整数对象,尤其是在循环中。
  • 使用生成器:假如只必要逐个处置惩罚整数,可以使用生成器而不是创建整个列表。
3. 整数的性能优化

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

  • 位操纵优化:对于大整数,CPython使用多精度算术,多精度整数在内存中以数组情势存储,每个元素代表肯定位数的数值。
关联的源码可参考:Include/cpython/longintrepr.h 和 Objects/longobject.c

  • 缓存机制优化:对于一些频繁出现的运算或者中心结果,会将其缓存起来。当再次必要这些结果时,直接从缓存中获取,而不是重新盘算。
关联的源码可参考:Objects/longobject.c 和 Objects/object.c

  • 并行盘算支持:对于大整数加法,会将盘算任务分解成多个子任务,并行地在多个核心上执行。
关联的源码可参考:Python/thread_pthread.h、Python/thread_pthread.c 和 Objects/longobject.c

  • 代码生成优化:在将整数加法的 Python 代码转换为机器码时,生成更高效的指令序列。
关联的源码可参考:Python/compile.c和Python/ceval.c
4. 总结

Python 的整数实现是一个高效且灵活的恣意精度整数系统。
通过CPython的源码,我们可以看到Python如何在内部处置惩罚大整数,以及如何通过优化策略提高性能和节流内存。
不过,虽然Python的整数实现已经非常强大,但在处置惩罚大量数据时,我们仍然可以通过一些技巧进一步优化内存使用和性能。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

麻花痒

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表