
[问题链接]( t/463777#reply8 )
由于 V2EX 回复没有 markdown 并且我感觉我的回复有开辟一个主题的资格
int _PyLong_Init(void) { #if NSMALLNEGINTS + NSMALLPOSINTS > 0 int ival, size; PyLongObject *v = small_ints; for (ival = -NSMALLNEGINTS; ival < NSMALLPOSINTS; ival++, v++) { size = (ival < 0) ? -1 : ((ival == 0) ? 0 : 1); if (Py_TYPE(v) == &PyLong_Type) { /* The element is already initialized, most likely * the Python interpreter was initialized before. */ Py_ssize_t refcnt; PyObject* op = (PyObject*)v; refcnt = Py_REFCNT(op) < 0 ? 0 : Py_REFCNT(op); _Py_NewReference(op); /* _Py_NewReference sets the ref count to 1 but * the ref count might be larger. Set the refcnt * to the original refcnt + 1 */ Py_REFCNT(op) = refcnt + 1; assert(Py_SIZE(op) == size); assert(v->ob_digit[0] == (digit)abs(ival)); } else { (void)PyObject_INIT(v, &PyLong_Type); } Py_SIZE(v) = size; v->ob_digit[0] = (digit)abs(ival); printf("这个数字是: %d \n 内存大小是: %d \n 地址是: %p\n",ival,sizeof(PyLongObject),v); } PyLongObject *copie = (PyLongObject*) PyLong_FromLong(1<<30); printf("%d\n",sizeof(copie->ob_digit)); printf("%d %d\n", copie->ob_digit[0],copie->ob_digit[1]); printf("这个数字是: %d \n 内存大小是: %d \n 地址是: %p\n",1<<30,sizeof(*copie),copie); 我们这里先 hack 一下 Python 源码 hack 部分就是带有 printf 的。
我们再看一下输出
地址是: 0x556ec899ecc0 这个数字是: 253 内存大小是: 32 地址是: 0x556ec899ece0 这个数字是: 254 内存大小是: 32 地址是: 0x556ec899ed00 这个数字是: 255 内存大小是: 32 地址是: 0x556ec899ed20 这个数字是: 256 内存大小是: 32 地址是: 0x556ec899ed40 4 0 1 这个数字是: 1073741824 内存大小是: 32 地址是: 0x7f671b8257b0 Python 3.6.5 (default, Jun 17 2018, 23:20:39) [GCC 8.1.1 20180531] on linux Type "help", "copyright", "credits" or "license" for more information. >>> 我要解释一下在 Python 源码中小整数是放在一个 static PyLongObject small_ints[NSMALLNEGINTS + NSMALLPOSINTS]; 数组中的。
我们看到的是 32 就是 PyLongObject 这个结构体的大小。
然后我们发现一个问题就是我们把创建一个很大的数字,我们看到的这个大小是不会变的。这里就是涉及到 Python3 整 数的存储问题了。
先看一下下面这个 C++ 代码
#include<stdio.h> #include<stdlib.h> int main() { struct test{ int a; int b[1]; }test1; printf("%d\n",sizeof(test1)); test* t = (test*) malloc(sizeof(test1)+sizeof(int)*5); printf("%d\n",sizeof(t)); } 这里我们 t 的数组 b 的容量肯定比 test 的数组 b 的容量大。
Desktop ./a.out 8 8 我们看到输出都是 8.这就是 Python 使用柔性数组导致的。有兴趣的可以去学习一下柔性数组。
In [1]: import sys In [2]: sys.getsizeof(0) Out[2]: 24 In [3]: sys.getsizeof(1) Out[3]: 28 In [4]: sys.getsizeof(2) Out[4]: 28 In [5]: sys.getsizeof(1<<30) Out[5]: 32 我们看到 1<<30 所占的字节数 比 1 占用的多了 4 个,1 比 0 多 4 个。 其实我们可以大胆的猜测一下:
看到这些我们就可以解释一下问题。
1 lance6716 2018-06-18 01:28:20 +08:00 via Android 感谢 po 深入挖掘。 另外我还是感觉到新用户水平下滑的很厉害,已经回归平庸大众水平了。看到 po 寻根问底的还是很开心 |
2 RLib 2018-06-18 01:40:10 +08:00 Python 2.7.14 (v2.7.14:84471935ed, Sep 16 2017, 20:25:58) [MSC v.1500 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> import sys >>> sys.getsizeof(0) 24 >>> sys.getsizeof(1) 24 >>> |
4 inframe 2018-06-18 04:14:41 +08:00 via Android py2 里面小整数从-5 到 256 都被缓冲了 看起来和 py3 有些不一样 |