代码我也仅仅是粗粗读了一遍, 可能出现疏漏和理解错误, 发现了望指出哈.
今天面了一家靠谱的创业公司, 可惜不是Python向的, 想继续玩Python是有代价的, 选择余地太窄了……
话说写文章很耗时间, 这个花了两个多小时….主要还是自个绘图渣效率低:(
准备找工作事宜很占时间, 后面只能慢慢来了(好像还很多很多的样子)
示例
1 2 3 4 5 6 7 8 9 10 11 |
>>> a = 1 >>> b = 1 >>> id(a) == id(b) True >>> c = 257 >>> d = 257 >>> id(c) == id(d) False #在python2.x中, 对于大的序列生成, 建议使用xrange(100000) 而不是range(100000), why? |
源码位置 Include/intobject.h |
Objects/intobject.c
PyIntObject
1 2 3 4 |
typedef struct { PyObject_HEAD long ob_ival; } PyIntObject; |
结构
几个构造方法
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# 从字符串, 生成PyIntObject对象 PyAPI_FUNC(PyObject *) PyInt_FromString(char*, char**, int); # 从Py_UNICODE, 生成PyIntObject对象 #ifdef Py_USING_UNICODE PyAPI_FUNC(PyObject *) PyInt_FromUnicode(Py_UNICODE*, Py_ssize_t, int); #endif # 从long值, 生成PyIntObject对象 PyAPI_FUNC(PyObject *) PyInt_FromLong(long); PyAPI_FUNC(PyObject *) PyInt_FromSize_t(size_t); PyAPI_FUNC(PyObject *) PyInt_FromSsize_t(Py_ssize_t); |
这几个方法, 只需要关注
1 2 |
# 因为大家最后都调用这个方法完成对象生成 PyAPI_FUNC(PyObject *) PyInt_FromLong(long); |
具体的构造方法 PyInt_FromLong
这个方法的定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
PyObject * PyInt_FromLong(long ival) { register PyIntObject *v; /* MARK: 如果, 值在小整数范围内, 直接从小整数对象池获取得到对象 */ #if NSMALLNEGINTS + NSMALLPOSINTS > 0 if (-NSMALLNEGINTS ival & ival NSMALLPOSINTS) { /* MARK: small_ints是什么后面说 */ v = small_ints[ival + NSMALLNEGINTS]; // 引用+1 Py_INCREF(v); /* 这里先忽略, 计数 */ #ifdef COUNT_ALLOCS if (ival >= 0) quick_int_allocs++; else quick_neg_int_allocs++; #endif // 返回 return (PyObject *) v; } #endif // 如果free_list还不存在, 或者满了 if (free_list == NULL) { // 新建一块PyIntBlock, 并将空闲空间链表头部地址给free_list if ((free_list = fill_free_list()) == NULL) // 如果失败, 返回 return NULL; } // 从free_list分出一个位置存放新的整数 /* Inline PyObject_New */ // 使用单向链表头位置 v = free_list; // free_list指向单向链表下一个位置 free_list = (PyIntObject *)Py_TYPE(v); // 初始化对象, 类型为PyInt_type, 值为ival PyObject_INIT(v, &PyInt_Type); v->ob_ival = ival; // 返回 return (PyObject *) v; } |
注意这里的Py_TYPE()
方法, 在我们第一篇文章里面有提到, 不知道的回去复习下对象的数据结构
1 |
#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type) |
简而言之:
1 2 3 4 5 |
1. 先判断数值是否是小整数, 是的话从小整数对象池里面直接返回 (这个池固定大小, 下一点讲) 2. 如果不是, 从通用整数对象池里面取一个, 初始化返回 (如果这时候通用整数对象池还不存在或者已经满了, 新建一个池加入维护. 通用整数对象池后面讲) |
小整数对象池
先看定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#ifndef NSMALLPOSINTS #define NSMALLPOSINTS 257 #endif #ifndef NSMALLNEGINTS #define NSMALLNEGINTS 5 #endif #if NSMALLNEGINTS + NSMALLPOSINTS > 0 /* References to small integers are saved in this array so that they can be shared. The integers that are saved are those in the range -NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive). */ static PyIntObject *small_ints[NSMALLNEGINTS + NSMALLPOSINTS]; #endif |
其实, 小整数对象池就是一个PyIntObject指针
数组(注意是指针数组), 大小=257+5=262, 范围是[-5, 257)
注意左闭右开. 即这个数组包含了262个指向PyIntObject的指针.
结构
创建整数时, 如果在[-5, 257)范围, 直接返回已经存在的整数对象指针, 所以我们看到开头的例子, id比较一个true/一个false
小整数对象池, 在一开始就初始化了, 其初始化代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
int _PyInt_Init(void) { PyIntObject *v; int ival; // 注意这里, free_list再次出现 #if NSMALLNEGINTS + NSMALLPOSINTS > 0 // 循环, 逐一生成 for (ival = -NSMALLNEGINTS; ival ob_ival = ival; // 放到数组里 small_ints[ival + NSMALLNEGINTS] = v; } #endif return 1; } |
代码很眼熟吧, 觉得不眼熟回上面看代码
结论
1 2 3 |
1. 小整数对象池缓存 [-5, 257) 内的整数对象, 数值在这个范围的整数对象有且只存在一个... 2. 小整数对象池, 只是一个指针数组, 其真正对象依赖通用整数对象池 |