为什么False值(0)比True(1)更小?

30

我在尝试使用sysgetsizeof()时发现False(或0)占用的字节数比True(或1)少。为什么呢?

import sys

print("Zero: " + str(sys.getsizeof(0)))
print("One: " + str(sys.getsizeof(1)))
print("False: " + str(sys.getsizeof(False)))
print("True: " + str(sys.getsizeof(True)))

# Prints:
# Zero: 24
# One: 28
# False: 24
# True: 28

事实上,其他数字(包括一些由多个数字组成的数字)也是28字节。

for n in range(0, 12):
  print(str(n) + ": " + str(sys.getsizeof(n)))

# Prints:
# 0: 24
# 1: 28
# 2: 28
# 3: 28
# 4: 28
# 5: 28
# 6: 28
# 7: 28
# 8: 28
# 9: 28
# 10: 28
# 11: 28

更令人惊奇的是:sys.getsizeof(999999999)也是28字节!然而,sys.getsizeof(9999999999)是32字节。

那到底发生了什么?我猜布尔型TrueFalse在内部分别转换为10,但为什么零的大小与其他较小的整数不同呢?

附带问题:这是否特定于Python(3)表示这些项的方式,还是数字在操作系统中通常都是这样表示的?


2
在Python2.7上,我得到了“24”的结果。我在3.6上重现了你的结果。这似乎是Python3特有的。 - jordanm
3
这是非常针对 Python 的。大多数编程语言没有任意精度整数,你必须选择像“int”、“long”、“long long”等数据类型,它们每个都有固定的大小。 - Barmar
@jordanm 这不完全是Python 3特定的问题 - 如果您比较0L1L等,您会在Python 2中看到相同的情况。不同之处在于,Python 2有单独的类型int(固定大小的32位有符号整数)和long(任意大小的整数),而Python 3将long重命名为int并且取消了int - abarnert
1个回答

32
请记住,Python的int值是任意大小的。那是如何工作的呢?
在CPython中,1一个int由一个PyLong_Object表示,它有一个4字节块2的数组,每个块都包含30位3数值。
  • 0不需要块。
  • 1 - (1<<30)-1需要1个块。
  • 1<<30 - (1<<60)-1需要2个块。
等等。
这略微有些简化;有关完整详情,请参见源代码中的longintrepr.h
在Python 2中,有两种不同的类型,称为intlong。一个int由C 32位有符号整数4直接嵌入头部表示,而不是一组块。一个long就像Python 3的int
如果你使用0L1L等进行相同的测试来明确要求long值,你将得到与Python 3相同的结果。但是,没有L后缀,适合32位的任何文字都会给你一个int,只有太大的文字才会给你long5(这意味着(1<<31)-1是一个int,但1<<31是一个2块的long。)

1. 在其他实现中,这可能不是真的。如果我没记错,Jython 做的事情与 CPython 大致相同,但 IronPython 使用了一个 C# 的“bignum”实现。

2. 为什么是 30 位而不是 32 位?主要是因为 pow** 的实现可以更简单、更快速,如果它可以假设两个“数字”中的位数可被 10 整除。

3. 它使用了 C 的 “struct hack”。从技术上讲,Py_LongObject 是 28 字节,但没有人分配 Py_LongObject ;他们 malloc 24、28、32、36 等字节,然后转换为 Py_LongObject *

4. 实际上,Python 的 int 是 C 的 long,只是为了让事情变得混乱。因此,C API 中充满了像 PyInt_FromLong 这样的东西,其中的 long 意味着“32 位 int”,以及像 PyLong_FromSize_t 这样的东西,其中的 long 意味着“bignum”。

5. Python 2.x 的早期版本没有很好地集成 intlong,但希望现在没有人需要担心这些问题了。


以非专业人士的语言来说,空的 PyLong_Object 占用 24 字节是有意义的吗?一个带有空数组的 PyLong_Object 等同于零。一旦你有一个大于 0 的整数,对象的数组就不再为空了,因此它的由 4 字节块组成的数组被填充,解释了尺寸的增加。 - Bram Vanroy
@BramVanroy 是的,没错(要深入理解它,你需要了解 C 语言中的“结构体黑科技”)。 - abarnert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接