python源码笔记_python源码学习笔记(二)
(二) python 繼承和多態
這非常類似C++的功能,只不過是是在C基礎上開發的。由上一節知,python的所有對象的基礎都是PyObject,所以例如創建一個PyIntObject對象,是通過PyObejct*變量來維護,所以在python內部各個函數之間傳遞的都是一種范型指針PyObject* ,是不是很像C++里面的基類。如果要Print(PyIntObject* ),由多態(polymophism)我們會知道,調用的實際上是PyIntObject對象對應的類型對象中定義的輸出操作。
看代碼:
longPyObject_Hash(PyObject*v) //注意是PyObject
{
PyTypeObject*tp = v->ob_type; //找到類型
if (tp->tp_hash !=NULL)return (*tp->tp_hash)(v); //調用相應類型的hash函數
/*To keep to the general practice that inheriting
* solely from object in C code should work without
* an explicit call to PyType_Ready, we implicitly call
* PyType_Ready here and then check the tp_hash slot again*/
//為了維持在C代碼單繼承中不直接調用PyType_Ready這一慣例,在這里間接地調用PyType_Ready(),并再次檢查tp_hash槽
if (tp->tp_dict ==NULL) {if (PyType_Ready(tp) < 0)return -1;if (tp->tp_hash !=NULL)return (*tp->tp_hash)(v);
}if (tp->tp_compare == NULL && RICHCOMPARE(tp) ==NULL) {return _Py_HashPointer(v); /*Use address as hash value*/ //把地址作為hash值返回
}/*If there's a cmp but no hash defined, the object can't be hashed*/
//如果有cmp,但是hash沒有被定義,返回這個對象不能被hash
returnPyObject_HashNotImplemented(v);
}
以PyIntObject為例,觀察其實現過程。
1 [intobject.h]2 typedef struct{3 PyObject_HEAD4 longob_ival;5 } PyIntObject;6
7 [intobject.c]8 static PyObject * //注意這里是靜態函數,而且是PyObject的指針,這個是多態的典型特征
9 int_add(PyIntObject *v, PyIntObject *w) //加
10 {11 register longa, b, x;12 CONVERT_TO_LONG(v, a);13 CONVERT_TO_LONG(w, b);14 /*casts in the line below avoid undefined behaviour on overflow*/
15 x = (long)((unsigned long)a +b);16 if ((x^a) >= 0 || (x^b) >= 0)17 returnPyInt_FromLong(x);18 return PyLong_Type.tp_as_number->nb_add((PyObject *)v, (PyObject *)w);19 }20
21 static PyObject *
22 int_sub(PyIntObject *v, PyIntObject *w) //減
23 {24 register longa, b, x;25 CONVERT_TO_LONG(v, a);26 CONVERT_TO_LONG(w, b);27 /*casts in the line below avoid undefined behaviour on overflow*/
28 x = (long)((unsigned long)a -b);29 if ((x^a) >= 0 || (x^~b) >= 0)30 returnPyInt_FromLong(x);31 return PyLong_Type.tp_as_number->nb_subtract((PyObject *)v,32 (PyObject *)w);33 }34
35 static PyObject *
36 int_mul(PyObject *v, PyObject *w) //乘
37 {38 longa, b;39 long longprod; /*a*b in native long arithmetic*/
40 double doubled_longprod; /*(double)longprod*/
41 double doubleprod; /*(double)a * (double)b*/
42
43 CONVERT_TO_LONG(v, a);44 CONVERT_TO_LONG(w, b);45 /*casts in the next line avoid undefined behaviour on overflow*/
46 longprod = (long)((unsigned long)a *b);47 doubleprod = (double)a * (double)b;48 doubled_longprod = (double)longprod;49
50 /*Fast path for normal case: small multiplicands, and no info51 is lost in either method.*/
52 if (doubled_longprod ==doubleprod)53 returnPyInt_FromLong(longprod);54
55 /*Somebody somewhere lost info. Close enough, or way off? Note56 that a != 0 and b != 0 (else doubled_longprod == doubleprod == 0).57 The difference either is or isn't significant compared to the58 true value (of which doubleprod is a good approximation).59 */
60 {61 const double diff = doubled_longprod -doubleprod;62 const double absdiff = diff >= 0.0 ? diff : -diff;63 const double absprod = doubleprod >= 0.0 ?doubleprod :64 -doubleprod;65 /*absdiff/absprod <= 1/32 iff66 32 * absdiff <= absprod -- 5 good bits is "close enough"*/
67 if (32.0 * absdiff <=absprod)68 returnPyInt_FromLong(longprod);69 else
70 return PyLong_Type.tp_as_number->nb_multiply(v, w);71 }72 }73
由此可知,python的int 實際上是C里面的long實現,所以加減乘除都是用long實現,又由于PyIntObject為一個Immutable對象,這個對象不可改變,因此在最后return的都是新的對象:PyInt_FromLong(num),即由long變量創建一個int變量。
(三)python整數的實現
在整數里,python分為大整數和小整數,為了加快計算,節省內存的分配時間,因為不論是什么對象,只要在堆上申請空間是非常費時的,所以在涉及到頻繁的內存操作時需要做一些優化。python提供了一種比較原始的方法——設個閾值,無語了,有這么來的么,最起碼來個動態閾值也好啊...
1 #ifndef NSMALLPOSINTS2 #define NSMALLPOSINTS 257
3 #endif
4 #ifndef NSMALLNEGINTS5 #define NSMALLNEGINTS 5
6 #endif 范圍設定到(-5~257) 在這個區間里面都為小整數
7 #if NSMALLNEGINTS + NSMALLPOSINTS > 0
8 /*References to small integers are saved in this array so that they //保存在數組里面被共享9 can be shared.10 The integers that are saved are those in the range11 -NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive). //[-5, 257)12 */
13 static PyIntObject *small_ints[NSMALLNEGINTS + NSMALLPOSINTS]; //申請(NSMALLNEGINTS + NSMALLPOSINTS)個PyIntObject* 為以后所共享
14 #endif
15
然后是大整數,大整數采用塊內存區間內緩存
1 #define BLOCK_SIZE 1000 /* 1K less typical malloc overhead */
2 #define BHEAD_SIZE 8 /* Enough for a 64-bit pointer */
3 #define N_INTOBJECTS ((BLOCK_SIZE - BHEAD_SIZE) / sizeof(PyIntObject))
4
5 struct_intblock {6 struct _intblock *next;7 PyIntObject objects[N_INTOBJECTS];8 };9
10 typedef struct_intblock PyIntBlock;11
12 static PyIntBlock *block_list =NULL;13 static PyIntObject *free_list = NULL;
N_INTOBJECTS ? ?到底是多少呢,算一下PyIntObject的大小,PyIntObject宏展開(without?Py_TRACE_REFS)后就是:
Py_ssize_t ?ob_refnt;
PyTypeObject *ob_type;
long ob_ival;
字節大小為4+4+8 = 16 ,?N_INTOBJECTS ?= (1000-8)/16 = 82,即一個PyIntBlock維護著的82個PyIntObeject,咋一看其實就是個單鏈表,因此這個82個objects相當于是82個數。
通過block_list?來維護,看代碼:
1 static PyIntObject *
2 fill_free_list(void)3 {4 PyIntObject *p, *q;5 /*Python's object allocator isn't appropriate for large blocks.*/
6 p = (PyIntObject *) PyMem_MALLOC(sizeof(PyIntBlock));7 if (p ==NULL)8 return (PyIntObject *) PyErr_NoMemory();9 ((PyIntBlock *)p)->next = block_list; //串聯blocklist
10 block_list = (PyIntBlock *)p;11 /*Link the int objects together, from rear to front, then return12 the address of the last int object in the block.*/
13 p = &((PyIntBlock *)p)->objects[0]; //第一塊即front,頭部,注意是地址哦,有個“&”
14 q = p + N_INTOBJECTS; //從0開始計數,q不是尾指針,還要減去1才是
15 while (--q > p) // //最后--q 后即rear,尾部從后往前遍歷
16 Py_TYPE(q) = (struct _typeobject *)(q-1); //將所有的PyIntObject 串起來
17 Py_TYPE(q) = NULL; //q現在為頭指針,類型置為空
18 return p + N_INTOBJECTS - 1; //返回rear
19 }
由上可知,相當于對外是一個blocklist, 對內是一系列的PyIntObject,當需要重新開辟blocklist時,通過((PyIntBlock *)p)->next = block_list把這些鏈表串起來。
現在我們可以看看PyInt_FromLong的實現了
1 [intobject.c]2 PyObject *
3 PyInt_FromLong(longival)4 {5 register PyIntObject *v; //用寄存器操作,加快速度
6 #if NSMALLNEGINTS + NSMALLPOSINTS > 0
7 if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS) { //在小數的范圍區間,直接命中
8 v = small_ints[ival +NSMALLNEGINTS];9 Py_INCREF(v); //引用計數加1
10 #ifdef COUNT_ALLOCS //這是要統計了
11 if (ival >= 0)12 quick_int_allocs++; //正數命中的個數
13 else
14 quick_neg_int_allocs++; //負數命中的個數
15 #endif
16 return (PyObject *) v;17 }18 #endif
19 if (free_list == NULL) { //如果是大數,且free_list 沒有被賦值,開始創建
20 if ((free_list = fill_free_list()) == NULL) //這里我們知道freelist指向的是鏈表的rear21 //和 block_list 是指向PyIntBlock 的指針相區別
22 returnNULL;23 }24 /*Inline PyObject_New*/
25 v =free_list;26 free_list = (PyIntObject *)Py_TYPE(v); //強制轉換一下類型變成PyIntObject類型
27 PyObject_INIT(v, &PyInt_Type); //初始化為python的int類型
28 v->ob_ival =ival;29 return (PyObject *) v;30 }31
最后有
1 [intobject.c]2 #define PyInt_CheckExact(op) ((op)->ob_type == &PyInt_Type)
3 ...4 static void
5 int_dealloc(PyIntObject *v)6 {7 if(PyInt_CheckExact(v)) {8 Py_TYPE(v) = (struct _typeobject *)free_list; //如果是整數類對象,只是簡單的把v置成free_list,即空閑鏈表的起點,相當于9 //覆蓋的形式
10 free_list =v;11 }12 else
13 Py_TYPE(v)->tp_free((PyObject *)v); //如果不是整數類型,調用底層的釋放函數
14 }15
16 static void
17 int_free(PyIntObject *v)18 {19 Py_TYPE(v) = (struct _typeobject *)free_list; //同上
20 free_list =v;21 }
不要搞混的是,上述實現不管是小數還是大數都是起著緩沖池的作用,不要誤解為是實現大數字功能,只不過都是用long實現的,這個很容易誤導。
再看小整數的換沖池:
1 static PyIntObject *small_ints[NSMALLNEGINTS + NSMALLPOSINTS]; //聲明為靜態指針數組
2 .........3
4 int
5 _PyInt_Init(void)6 {7 PyIntObject *v;8 intival;9 #if NSMALLNEGINTS + NSMALLPOSINTS > 0
10 for (ival = -NSMALLNEGINTS; ival < NSMALLPOSINTS; ival++) {11 if (!free_list && (free_list = fill_free_list()) == NULL) //同樣是申請空閑鏈表
12 return 0;13 /*PyObject_New is inlined*/
14 v =free_list;15 free_list = (PyIntObject *)Py_TYPE(v);16 PyObject_INIT(v, &PyInt_Type);17 v->ob_ival = ival; //賦值后加入small_ints這個緩沖池
18 small_ints[ival + NSMALLNEGINTS] = v; //相當于一個一一映射的關系
19 }20 #endif
21 return 1;22 }
我們知道的就是說所有的整數都在堆里面有內存,小整數使用通過small_ints[]數組一一映射加快查找速度,大數則需要通過鏈表來維護內存。
總結
以上是生活随笔為你收集整理的python源码笔记_python源码学习笔记(二)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 体验生活是什么意思
- 下一篇: vue 传递多行数据_vue父组件向子组