说到计算机中的整型,相信很多人都会联想到32位整型(或者int),是程序员日常生活中用的最多的一种类型。32位整型顾名思义,占用32个位也就是4个字节,取值范围−2,147,483,648~ 2,147,483,647 。C/C++中是4个字节,Java中也是4个字节,但是Python中呢?
我们知道Python中也有int类,而且非常好用,原生支持高精度计算。但是Python中的一个整型到底占用多少字节呢?我相信绝大多数的Python程序员从未想过这一点,越是习以为常的东西越是不会在意它的存在。
在Python中,如果想要知道一个对象所占用的内存大小,只需要使用sys.getsizeof
这个API就可以了。那就让我们来试一下不同的整数
1 2 3 4 5 6 |
print(sys.getsizeof(0)) # 24 print(sys.getsizeof(1)) # 28 print(sys.getsizeof(2)) # 28 print(sys.getsizeof(2**15)) # 28 print(sys.getsizeof(2**30)) # 32 print(sys.getsizeof(2**128)) # 44 |
从上面的小实验可以看出,一个整型最少要占24字节(0),1开始就要占用28个字节,到了2的30次方开始要占用32个字节,而2的128次方则要占用44个字节。我们可以得到两点规律,1. 字节数随着数字增大而增大。2. 每次的增量是4个字节。 好像至此已经回答了我们的题目中的问题:Python中的整型占多少个字节?答案是:变长的(相对于int32的定长),而且最少24个字节。
你以为本文到这里就结束了吗?那你就图样图森破了。一个整型数2,居然要占用28个字节!这完全了颠覆了我的认知,我一定搞清楚为什么。 在哥们的帮助下,我找到了Python的源码。 https://github.com/python/cpython
Python的官方实现是C语言,所以叫cpython。这也就意味着只要Python还在,C就会不消失。其他实现还有jython(Java), IronPython (.Net), PyPy (Python)。
第一件事情要搞清楚的是Python中int类型在cpython的名字是什么?看了半天,在 longobject.h 中发现了一个叫 PyLongObject
的结构体。然而它只是一个马甲,是 _longobject
的别名。在longintrepr.h中找到了 _longobject
的定义如下:
1 2 3 4 |
struct _longobject { PyObject_VAR_HEAD digit ob_digit[1]; }; |
在文件的开头就看到了typedef uint32_t digit;
,digit
就是unit32_t
, 每个元素占4个字节。但PyObject_VAR_HEAD
又是什么鬼?在object.h中发现了它是个宏,上面的注释倒是挺有意思的。
1 2 3 4 5 6 7 |
/* PyObject_VAR_HEAD defines the initial segment of all variable-size container objects. These end with a declaration of an array with 1 element, but enough space is malloc'ed so that the array actually has room for ob_size elements. Note that ob_size is an element count, not necessarily a byte count. */ define PyObject_VAR_HEAD PyVarObject ob_base; |
等一下,PyVarObject
又是什么?还好定义就在下面。
1 2 3 4 |
typedef struct { PyObject ob_base; Py_ssize_t ob_size; /* Number of items in variable part */ } PyVarObject; |
又一层嵌套,是不是已经晕了,继续查看PyObject
的定义,这次反而在上面了。
1 2 3 4 5 |
typedef struct _object { _PyObject_HEAD_EXTRA Py_ssize_t ob_refcnt; struct _typeobject *ob_type; } PyObject; |
有完没完啊?_PyObject_HEAD_EXTRA
又是什么?看了一下发现它只在debug build中有定义,这里就不展开了。Py_ssize_t
等于ssize_t
如果有定义的话, ssize_t
在64位的机器上就是long
。_typeobject
又是什么?感觉应该非常大,不然就不会用指针了。不过话说回来,既然用了指针,我又何必去关心它是什么呢?反正就是8个字节而已,指向一个内存地址。至此真相大了一个白,如果我们把structs flatten, PyLongObject
定义如下:
1 2 3 4 5 6 |
struct PyLongObject { long ob_refcnt; // 8 bytes struct _typeobject *ob_type; // 8 bytes long ob_size; // 8 bytes unsigned int ob_digit[1]; // 4 bytes * abs(ob_size) }; |
ob_refcnt
引用计数 8个字节,ob_type
类型信息 8个字节(指针),ob_size
变长部分元素的个数,8个字节。ob_digit
变长的数据部分,字节数为4*abs(ob_size
),ob_size
可以为0,所以最少8+8+8=24字节,每次增量都是4 (unsigned int
) 的倍数。这和我们之前观察到的实验结果吻合。
以上都是基于64位的Python,对于32位的版本,定义如下:
1 2 3 4 5 6 |
struct PyLongObject { int ob_refcnt; // 4 bytes struct _typeobject *ob_type; // 4 bytes int ob_size; // 4 bytes unsigned short ob_digit[1]; // 2 bytes * abs(ob_size) }; |
32位就要比64位小很多了,最少12个字节,增量为2个字节。
好了,今天就写到这里。相信你对整型数或者Python有了一个新的认识。下面一篇我们会将会介绍整型数在Python中的表示和计算。
请尊重作者的劳动成果,转载请注明出处!花花保留对文章/视频的所有权利。
如果您喜欢这篇文章/视频,欢迎您捐赠花花。
If you like my articles / videos, donations are welcome.
[…] 看到输出效果,属实让人震惊,一个int型的数值,居然用高达24个字节来存储,而且在电脑存储巨细居然是不限制的,是自增进的。喝口水压压惊后,让我想到c++的STL容器,可以使用栈顶指针,当检测到容量超出时,则删除旧内存而去开拓一块新的内存,确实可以实现这种效果。 扯完犊子,那么这里首先先解决第一个问题,int类型这个变量什么时刻内存会变大?我在这篇博客中提到的文章找到了谜底: 点此处跳转。重点就是下面这张图,简朴来说就是int类型每多2^30(1073741824 )就会增添四个字节。这也验证了上面例子getsizeof(2**30)是32字节,而比它小的是28个字节的缘故原由,固然零除外。其他类型也可以在下面找到谜底。 那么它的自增进问题呢,这个可能要去看python的源码才气解决,还好有大佬已经提前给我们铺了下路,这里我就没这个能力去领会太深入了,直接引用大佬的结论就可以了。详细可以参考这篇文章:点此处跳转。在64位python的注释器中,int类型的界说是通过一个结构体来界说的,简化后的结构体如下所示: […]
[…] 看到输出结果,属实让人震惊,一个int型的数值,居然用高达24个字节来存储,而且在电脑存储大小居然是不限定的,是自增长的。喝口水压压惊后,让我想到c++的STL容器,可以使用栈顶指针,当检测到容量超出时,则删除旧内存而去开辟一块新的内存,确实可以实现这种效果。 扯完犊子,那么这里首先先解决第一个问题,int类型这个变量什么时候内存会变大?我在这篇博客中提到的文章找到了答案: 点此处跳转。重点就是下面这张图,简单来说就是int类型每多2^30(1073741824 )就会增加四个字节。这也验证了上面例子getsizeof(2**30)是32字节,而比它小的是28个字节的原因,当然零除外。其他类型也可以在下面找到答案。 那么它的自增长问题呢,这个可能要去看python的源码才能解决,还好有大佬已经提前给我们铺了下路,这里我就没这个能力去了解太深入了,直接引用大佬的结论就可以了。具体可以参考这篇文章:点此处跳转。在64位python的解释器中,int类型的定义是通过一个结构体来定义的,简化后的结构体如下所示: […]
[…] 看到输出结果,属实让人震惊,一个int型的数值,居然用高达24个字节来存储,而且在电脑存储大小居然是不限定的,是自增长的。喝口水压压惊后,让我想到c++的STL容器,可以使用栈顶指针,当检测到容量超出时,则删除旧内存而去开辟一块新的内存,确实可以实现这种效果。 扯完犊子,那么这里首先先解决第一个问题,int类型这个变量什么时候内存会变大?我在这篇博客中提到的文章找到了答案: 点此处跳转。重点就是下面这张图,简单来说就是int类型每多2^30(1073741824 )就会增加四个字节。这也验证了上面例子getsizeof(2**30)是32字节,而比它小的是28个字节的原因,当然零除外。其他类型也可以在下面找到答案。 那么它的自增长问题呢,这个可能要去看python的源码才能解决,还好有大佬已经提前给我们铺了下路,这里我就没这个能力去了解太深入了,直接引用大佬的结论就可以了。具体可以参考这篇文章:点此处跳转。在64位python的解释器中,int类型的定义是通过一个结构体来定义的,简化后的结构体如下所示: […]
[…] 看到输出结果,属实让人震惊,一个int型的数值,居然用高达24个字节来存储,而且在电脑存储大小居然是不限定的,是自增长的。喝口水压压惊后,让我想到c++的STL容器,可以使用栈顶指针,当检测到容量超出时,则删除旧内存而去开辟一块新的内存,确实可以实现这种效果。 扯完犊子,那么这里首先先解决第一个问题,int类型这个变量什么时候内存会变大?我在这篇博客中提到的文章找到了答案: 点此处跳转。重点就是下面这张图,简单来说就是int类型每多2^30(1073741824 )就会增加四个字节。这也验证了上面例子getsizeof(2**30)是32字节,而比它小的是28个字节的原因,当然零除外。其他类型也可以在下面找到答案。 那么它的自增长问题呢,这个可能要去看python的源码才能解决,还好有大佬已经提前给我们铺了下路,这里我就没这个能力去了解太深入了,直接引用大佬的结论就可以了。具体可以参考这篇文章:点此处跳转。在64位python的解释器中,int类型的定义是通过一个结构体来定义的,简化后的结构体如下所示: […]
[…] 看到输出结果,属实让人震惊,一个int型的数值,居然用高达24个字节来存储,而且在电脑存储大小居然是不限定的,是自增长的。喝口水压压惊后,让我想到c++的STL容器,可以使用栈顶指针,当检测到容量超出时,则删除旧内存而去开辟一块新的内存,确实可以实现这种效果。 扯完犊子,那么这里首先先解决第一个问题,int类型这个变量什么时候内存会变大?我在这篇博客中提到的文章找到了答案: 点此处跳转。重点就是下面这张图,简单来说就是int类型每多2^30(1073741824 )就会增加四个字节。这也验证了上面例子getsizeof(2**30)是32字节,而比它小的是28个字节的原因,当然零除外。其他类型也可以在下面找到答案。 那么它的自增长问题呢,这个可能要去看python的源码才能解决,还好有大佬已经提前给我们铺了下路,这里我就没这个能力去了解太深入了,直接引用大佬的结论就可以了。具体可以参考这篇文章:点此处跳转。在64位python的解释器中,int类型的定义是通过一个结构体来定义的,简化后的结构体如下所示: […]